运行时动态建表
- 底层构建一个creatMysqlTablesMapper xml文件与creatMysqlTablesMapper 接口映射
- 实现了以下几个方法:
- 创建表
- 根据表名查询该表是否存在
- 根据表名查询库中该表的字段结构
- 增加字段
- 根据表名删除表
- 自动建表过程,首先获取表后缀(当前用户的accessKeyId)
- 扫描要自动建表的pojo包,获取class对象集合
- 创建两个map,一个是更新表map,一个是创建表map
- key:要创建或更新表的表名
- value:要创建或更新表的字段信息列表
(该对象封装了字段名、字段类型、是否为主键、是否可以为空等属性)
- 构建两个map
- 遍历原来得到的class对象
- 根据当前用户的accessKeyId和class对象拼接要生成表的表名
- 调用底层mapper根据表名查看当前表是否已经存在
- 表不存在需要新建
- 这时要根据当前class对象生成一个字段信息列表
- 用反射获取class对象的字段表,将每个字段都封装成一个commonColumn对象,存到新的字段信息表当中
- 如何获取某个字段创建时的属性(not null,是否为主键等信息) 创建pojo的时候通过jpa的@column注解标注这些属性,jpa可以读取这些属性信息。
- 将这个字段信息列表放到新建表的map当中去
- 这时要根据当前class对象生成一个字段信息列表
- 表已经存在,需要更新
- 对应当前的class对象生成一个字段信息表
- 调用底层mapper返回原本存在表的字段信息
- 对比新生成的字段信息表和原来的字段信息
- 如果有不同的字段信息,就将该字段信息对象存到新增字段信息列表中
- 如果新增字段信息列表长度不为0,就把当前字段信息列表传到map当中去
- 表不存在需要新建
- 遍历所有的class对象就得到完整新增表map和更新表map了
- 分别新增表和更新表
- 新增表调用底层的mapper的新增表方法,根据map里面的表名和字段信息名可以拼接出创建表的sql语句
- 调用底层mapper的更新表方法,同样能够通过map里面的内容拼接更新表的sql语句(用一个tablesql对象来代表拼接的语句,列也有一个columnsql来代表创建列拼接的语句)
动态表名如何实现
使用mybatis-plus 动态表名插件,用法如下:
mybatis-plus提供了一个接口TableNameHandler,作为表名处理器,其中有个dynamicTableName方法,重写这个方法,返回值就是表名。
- 创建一个配置类,比如说就叫myBatisPlusConfig
- 里面创建一个mybatisPlusInterceptor拦截器对象,并将其注册到容器当中
- mybatisPlusInterceptor 对象可以添加内部拦截器,mybatis-plus提供的动态表名内部拦截器,这个拦截器可以设置表和表名处理器的映射关系(map)
- myBatisPlus提供了TableNameHandler接口,实现这个接口并重写其中的dynamicTableName方法,就可以实现自己的动态表名逻辑,返回值就是表名。
- 我得动态表名逻辑,在config中创建一个静态的threadlocal,用来存储动态的表名。在动态表名处理器里面,我先创建一个局部变量myTableName把threadlocal值保存起来,我再把threadlocal值设为null,然后返回这个局部变量。这个局部变量就是当前数据库操作的表名。
为什么要用aop呢,因为所有的表名对应的都是同一个threadlocal,我需要在每次调用处理数据的service之前都重新设置一遍表名。使用aop的前置通知可以解决这个问题。
spring怎么使用aop
三个步骤
使用@aspect和@component注解定义切面
- 使用@Pointcut定义切入点 后面用execution定义具体哪个包下的类的哪些方法
- 使用@after before 等定义是什么类型的通知,通知下面是具体的aop业务逻辑
aop基本概念
- 接入点:被接入扩展功能的连接点
- 通知:具体业务逻辑
- 连接点:允许使用通知的地方
- 切面:切入点和通知的结合
-
aop底层原理
jdk动态代理
- 被代理类要实现一个接口
- 创建一个动态代理处理器实现invocationHandler,重写其中的invoke方法,需要增加的功能在invoke中实现。
- invocationHandler里面要创建一个object类的成员变量,作为将被代理的真实对象
- invoke方法有三个参数
- proxy:代理对象,该参数的作用
- 这个是动态生成的代理对象,这个参数表示我们可以在invoke方法里面通过反射获取代理对象的信息,从而进行一些判断什么的
- 我们可以在invoke把代理对象返回
- method:被代理类的method对象
- args:被代理类被代理方法的参数
- method.invoke(args)就是执行被代理类的方法
- proxy:代理对象,该参数的作用
- 生成被代理类使用Proxy这个动态代理工具类,一般使用它的newProxyInstence方法结合多态生成一个动态代理类对象,动态代理类是被动态代理类接口的实现类。
- newProxyInstence方法三个参数
- 类加载器
- 类接口集合
- invokcationHandler
- newProxyInstence方法三个参数
- 动态代理类对象调用相应的方法完成动态代理。
本质是创建一个与被代理类实现相同接口的代理类,底层通过调用被代理类的method对象,使用被代理类的方法。
cglib动态代理
- 创建一个cglib代理类实现MethodInterceptor接口
- 重写接口中的intercept方法,也是和invoke方法差不多调用了
- 写一个生成代理类对象的方法,返回被代理类的子类对象
- 子类对象的方法覆盖了被代理类的方法,实现扩展
本质是修改代理类对象字节码,生成一个被代理类的子类重写父类的方法。