insertintoselect主键⾃增_mybatis实现主键⾃动⽣成前⾔
前阵⼦和朋友聊天,他说他们项⽬有个需求,要实现主键⾃动⽣成,不想每次新增的时候,都⼿动设置主键。于是我就问他,那你们数据库表设置主键⾃动递增不就得了。他的回答是他们项⽬⽬前的id都是采⽤雪花算法来⽣成,因此为了项⽬稳定性,不会切换id的⽣成⽅式。
朋友问我有没有什么实现思路,他们公司的orm框架是mybatis,我就建议他说,不然让你⽼⼤把mybatis切换成mybatis-plus。
mybatis-plus就⽀持注解式的id⾃动⽣成,⽽且mybatis-plus只是对mybatis进⾏增强不做改变。朋友还是那句话,说为了项⽬稳定,之前项⽬组没有使⽤mybatis-plus的经验,贸然切换不知道会不会有什么坑。后⾯没招了,我就跟他说不然你⽤mybatis的实现⼀个吧。于是⼜有⼀篇吹⽔的创作题材出现。
前置知识
在介绍如何通过mybatis实现主键⾃动⽣成之前,我们先来梳理⼀些知识点
1、mybatis的作⽤
mybatis设计的初衷就是为了供⽤户在某些时候可以实现⾃⼰的逻辑⽽不必去动mybatis固有的逻辑
2、Interceptor
每个⾃定义都要实现
org.apache.ibatis.plugin.Interceptor
这个接⼝,并且⾃定义类上添加@Intercepts注解
3、能拦截哪些类型
Executor:拦截执⾏器的⽅法。ParameterHandler:拦截参数的处理。ResultHandler:拦截结果集的处理。StatementHandler:拦截Sql语法构建的处理。
4、拦截的顺序
a、不同类型的执⾏顺序
Executor -> ParameterHandler -> StatementHandler -> ResultSetHandler
b、多个拦截同种类型同⼀个⽬标⽅法,执⾏顺序是后配置的先执⾏
⽐如在mybatis配置如下
则InterceptorB先执⾏。
如果是和spring做了集成,先注⼊spring ioc容器的,则后执⾏。⽐如有个mybatisConfig,⾥⾯有如下bean配置
@Bean    public InterceptorA interceptorA(){        return new InterceptorA();    }    @Bean    public InterceptorB interceptorB(){        return new InterceptorB();    }则InterceptorB先执⾏。当然如果你是直接⽤@Component注解这形式,则可以配合@Order注解来控制加载顺序
5、注解介绍
@Intercepts:标识该类是⼀个
@Signature:指明⾃定义需要拦截哪⼀个类型,哪⼀个⽅法。
@Signature注解属性中的type表⽰对应可以拦截四种类型(Executor、ParameterHandler、ResultHandler、StatementHandler)中的
⼀种;method表⽰对应类型(Executor、ParameterHandler、ResultHandler、StatementHandler)中的哪类⽅法;args表⽰对应
method中的参数类型
6、⽅法介绍
a、 intercept⽅法
public Object intercept(Invocation invocation) throws Throwable
这个⽅法就是我们来执⾏我们⾃⼰想实现的业务逻辑,⽐如我们的主键⾃动⽣成逻辑就是在这边实现。
河内塔最少移动次数
Invocation这个类中的成员属性target就是@Signature中的type;method就是@Signature中的method;args就是@Signature中的
args参数类型的具体实例对象
b、 plugin⽅法
public Object plugin(Object target)
eclipse是安装jre还是jdk
这个是⽤返回代理对象或者是原⽣代理对象,如果你要返回代理对象,则返回值可以设置为
Plugin.wrap(target, this);this为
如果返回是代理对象,则会执⾏的业务逻辑,如果直接返回target,就是没有的业务逻辑。说⽩了就是告诉mybatis是不是要
进⾏拦截,如果要拦截,就⽣成代理对象,不拦截是⽣成原⽣对象
c、 setProperties⽅法
public void setProperties(Properties properties)
⽤于在Mybatis配置⽂件中指定⼀些属性
主键⾃动⽣成思路
1、定义⼀个
主要拦截
`Executor#update(MappedStatement ms, Object parameter)`}
这个⽅法。mybatis的insert、update、delete都是通过这个⽅法,因此我们通过拦截这个这⽅法,来实现主键⾃动⽣成。其代码块如下
@Intercepts(value={@Signature(type = Executor.class,method = "update",args = {MappedStatement.class,Object.class})})public class AutoIdInterceptor impleme
2、判断sql操作类型
Executor 提供的⽅法中,update 包含了 新增,修改和删除类型,⽆法直接区分,需要借助 MappedStatement 类的属性excel+python
SqlCommandType 来进⾏判断,该类包含了所有的操作类型
public enum SqlCommandType {  UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;}
当SqlCommandType类型是insert我们才进⾏主键⾃增操作
3、填充主键值
3.1、编写⾃动⽣成id注解
Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface AutoId {    /**    * 主键名    * @return    */    String primaryKe
3.2、 雪花算法实现
我们可以直接拿hutool这个⼯具包提供的idUtil来直接实现算法。
引⼊
cn.hutool            hutool-all
Snowflake snowflake = ateSnowflake(0,0);long value = Id();
3.3、填充主键值
其实现核⼼是利⽤反射。其核⼼代码⽚段如下
ReflectionUtils.Class(), field->{                    ReflectionUtils.makeAccessible(field);                    AutoId autoId = Annotation(AutoId.c public
class SnowFlakeAutoIdProcess extends BaseAutoIdProcess {    private static Snowflake snowflake = ateSnowflake(0,0);    public SnowFlakeAutoId 如果项⽬中的l已经的insert语句已经含有id,⽐如
insert into sys_test( `id`,`type`, `url`,`menu_type`,`gmt_create`)values( #{id},#{type}, #{url},#{menuType},#{gmtCreate})
则只需到填充id值这⼀步。的任务就完成。如果l的insert不含id,形如
insert into sys_test( `type`, `url`,`menu_type`,`gmt_create`)values( #{type}, #{url},#{menuType},#{gmtCreate})
则还需重写insert语句以及新增id参数
4、重写insert语句以及新增id参数(可选)
4.1 重写insert语句
⽅法⼀:
从 MappedStatement 对象中获取 SqlSource 对象,再从从 SqlSource 对象中获取获取 BoundSql 对象,通过 BoundSql#getSql ⽅
法获取原始的sql,最后在原始sql的基础上追加id
⽅法⼆:
引⼊
com.alibabadruid${druid.version}
通过
com.alibaba.druid.sql.parser.MySqlStatementParser
获取相应的表名、需要insert的字段名。然后重新拼凑出新的insert语句
4.2 把新的sql重置给Invocation
其核⼼实现思路是创建⼀个新的MappedStatement,新的MappedStatement绑定新sql,再把新的MappedStatement赋值给
Invocation的args[0],代码⽚段如下
private void resetSql2Invocation(Invocation invocation, BoundSqlHelper boundSqlHelper,Object entity) throws SQLException {        final Object[] args = invocation
4.3 新增id参数
其核⼼是利⽤
org.apache.ibatis.mapping.ParameterMapping
核⼼代码⽚段如下
private void setPrimaryKeyParaterMapping(String primaryKey) {          ParameterMapping parameterMapping = new ParameterMapping.Builder(boundSqlHelpe
5、将mybatis注⼊到spring容器
可以直接在上加
@org.springframework.stereotype.Component
注解。也可以通过
@Bean    public AutoIdInterceptor autoIdInterceptor(){        return new AutoIdInterceptor();    }
mysql语句的执行顺序6、在需要实现⾃增主键的实体字段上加如下注解
@AutoId(primaryKey = "id")private Long id;
测试
1、对应的测试实体以及单元测试代码如下
@Datapublic class TestDO implements Serializable {private static final long serialVersionUID = 1L;@AutoId(primaryKey = "id")private Long id;private Integer type; @Autowired    private TestService testService;    @Test    public void testAdd(){        TestDO testDO = new TestDO();        testDO.setType(1);        testDO.setMen
个人网站免费服务器2、当mapper的insert语句中含有id,形如下
insert into sys_test(`id`,`type`, `url`,`menu_type`,`gmt_create`)values( #{id},#{type}, #{url},#{menuType},#{gmtCreate})
以及批量插⼊sql
insert into sys_test( `id`,`gmt_create`,`type`,`url`,`menu_type`)values( #{test.id},#{Create},#{pe}, #{test.url},#{uType})
查看控制台sql打印语句
c语言程序设计钟志水版答案15:52:04 [main] DEBUG com.lybgeek.dao.TestDao.save - ==>  Preparing: insert into sys_test(`id`,`type`, `url`,`menu_type`,`gmt_create`) values( ?,?, ?,?,? ) 15:5 15:52:04 [main] lybgeek.dao.TestDao.saveBatch - ==>  Preparing: insert into sys_test( `id`,`gmt_create`,`type`,`url`,`menu_type`) values ( ?,?,?, ?, ?)查看数据库
3、当mapper的insert语句中不含id,形如下
insert into sys_test(`type`, `url`,`menu_type`,`gmt_create`)values(#{type}, #{url},#{menuType},#{gmtCreate})
以及批量插⼊sql

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。