日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

insert into select 主键自增_springboot2结合mybatis拦截器实现主键自动生成

發(fā)布時(shí)間:2024/1/23 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 insert into select 主键自增_springboot2结合mybatis拦截器实现主键自动生成 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

點(diǎn)擊上方藍(lán)字關(guān)注我們

1

01

前言

前陣子和朋友聊天,他說他們項(xiàng)目有個(gè)需求,要實(shí)現(xiàn)主鍵自動(dòng)生成,不想每次新增的時(shí)候,都手動(dòng)設(shè)置主鍵。于是我就問他,那你們數(shù)據(jù)庫(kù)表設(shè)置主鍵自動(dòng)遞增不就得了。他的回答是他們項(xiàng)目目前的id都是采用雪花算法來生成,因此為了項(xiàng)目穩(wěn)定性,不會(huì)切換id的生成方式。

朋友問我有沒有什么實(shí)現(xiàn)思路,他們公司的orm框架是mybatis,我就建議他說,不然讓你老大把mybatis切換成mybatis-plus。mybatis-plus就支持注解式的id自動(dòng)生成,而且mybatis-plus只是對(duì)mybatis進(jìn)行增強(qiáng)不做改變。朋友還是那句話,說為了項(xiàng)目穩(wěn)定,之前項(xiàng)目組沒有使用mybatis-plus的經(jīng)驗(yàn),貿(mào)然切換不知道會(huì)不會(huì)有什么坑。后面沒招了,我就跟他說不然你用mybatis的攔截器實(shí)現(xiàn)一個(gè)吧。于是又有一篇吹水的創(chuàng)作題材出現(xiàn)。

1

02

前置知識(shí)

在介紹如何通過mybatis攔截器實(shí)現(xiàn)主鍵自動(dòng)生成之前,我們先來梳理一些知識(shí)點(diǎn)

mybatis攔截器的作用

mybatis攔截器設(shè)計(jì)的初衷就是為了供用戶在某些時(shí)候可以實(shí)現(xiàn)自己的邏輯而不必去動(dòng)mybatis固有的邏輯

Interceptor攔截器

每個(gè)自定義攔截器都要實(shí)現(xiàn)

org.apache.ibatis.plugin.Interceptor

這個(gè)接口,并且自定義攔截器類上添加@Intercepts注解

攔截器能攔截哪些類型

Executor:攔截執(zhí)行器的方法。ParameterHandler:攔截參數(shù)的處理。ResultHandler:攔截結(jié)果集的處理。StatementHandler:攔截Sql語(yǔ)法構(gòu)建的處理。

攔截的順序a、不同類型攔截器的執(zhí)行順序Executor?-> ParameterHandler -> StatementHandler -> ResultSetHandlerb、多個(gè)攔截器攔截同種類型同一個(gè)目標(biāo)方法,執(zhí)行順序是后配置的攔截器先執(zhí)行

比如在mybatis配置如下

<plugins>
????<plugin?interceptor="com.lybgeek.InterceptorA"?/>
????<plugin?interceptor="com.lybgeek.InterceptorB"?/>
??plugins>

則InterceptorB先執(zhí)行。

如果是和spring做了集成,先注入spring ioc容器的攔截器,則后執(zhí)行。比如有個(gè)mybatisConfig,里面有如下攔截器bean配置

? ? @Bean
????public?InterceptorA interceptorA(){
????????return?new?InterceptorA();
????}

????@Bean
????public?InterceptorB interceptorB(){
????????return?new?InterceptorB();
????}

則InterceptorB先執(zhí)行。當(dāng)然如果你是直接用@Component注解這形式,則可以配合@Order注解來控制加載順序

攔截器注解介紹

@Intercepts:標(biāo)識(shí)該類是一個(gè)攔截器

@Signature:指明自定義攔截器需要攔截哪一個(gè)類型,哪一個(gè)方法。
@Signature注解屬性中的type表示對(duì)應(yīng)可以攔截四種類型(Executor、ParameterHandler、ResultHandler、StatementHandler)中的一種;method表示對(duì)應(yīng)類型(Executor、ParameterHandler、ResultHandler、StatementHandler)中的哪類方法;args表示對(duì)應(yīng)method中的參數(shù)類型

攔截器方法介紹a、?intercept方法public?Object?intercept(Invocation invocation) throws Throwable

這個(gè)方法就是我們來執(zhí)行我們自己想實(shí)現(xiàn)的業(yè)務(wù)邏輯,比如我們的主鍵自動(dòng)生成邏輯就是在這邊實(shí)現(xiàn)。

Invocation這個(gè)類中的成員屬性target就是@Signature中的type;method就是@Signature中的method;args就是@Signature中的args參數(shù)類型的具體實(shí)例對(duì)象

b、?plugin方法public?Object?plugin(Object?target)

這個(gè)是用返回代理對(duì)象或者是原生代理對(duì)象,如果你要返回代理對(duì)象,則返回值可以設(shè)置為

Plugin.wrap(target, this);
this為攔截器

如果返回是代理對(duì)象,則會(huì)執(zhí)行攔截器的業(yè)務(wù)邏輯,如果直接返回target,就是沒有攔截器的業(yè)務(wù)邏輯。說白了就是告訴mybatis是不是要進(jìn)行攔截,如果要攔截,就生成代理對(duì)象,不攔截是生成原生對(duì)象

c、?setProperties方法public?void?setProperties(Properties properties)

用于在Mybatis配置文件中指定一些屬性

1

03

主鍵自動(dòng)生成思路

定義一個(gè)攔截器

主要攔截

Executor#update(MappedStatement ms, Object parameter)`}

這個(gè)方法。mybatis的insert、update、delete都是通過這個(gè)方法,因此我們通過攔截這個(gè)這方法,來實(shí)現(xiàn)主鍵自動(dòng)生成。其代碼塊如下

@Intercepts(value={@Signature(type = Executor.class,method = "update",args = {MappedStatement.class,Object.class})})
public class AutoIdInterceptor implements Interceptor {}判斷sql操作類型

Executor 提供的方法中,update 包含了 新增,修改和刪除類型,無法直接區(qū)分,需要借助 MappedStatement 類的屬性?SqlCommandType?來進(jìn)行判斷,該類包含了所有的操作類型

public?enum?SqlCommandType {
??UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
}

當(dāng)SqlCommandType類型是insert我們才進(jìn)行主鍵自增操作

填充主鍵值a、編寫自動(dòng)生成id注解Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public?@interface?AutoId {
????/**
?????* 主鍵名
?????* @return
?????*/
????String?primaryKey();

????/**
?????* 支持的主鍵算法類型
?????* @return
?????*/
????IdType type() default?IdType.SNOWFLAKE;

????enum?IdType{
????????SNOWFLAKE
????}
}

b、?雪花算法實(shí)現(xiàn)

我們可以直接拿hutool這個(gè)工具包提供的idUtil來直接實(shí)現(xiàn)算法。

<dependency>
????????????<groupId>cn.hutoolgroupId>
????????????<artifactId>hutool-allartifactId>
????????dependency>Snowflake snowflake = IdUtil.createSnowflake(0,0);
long?value?= snowflake.nextId();

c、填充主鍵值

其實(shí)現(xiàn)核心是利用反射。其核心代碼片段如下

ReflectionUtils.doWithFields(entity.getClass(), field->{
????????????????????ReflectionUtils.makeAccessible(field);
????????????????????AutoId autoId = field.getAnnotation(AutoId.class);
????????????????????if(!ObjectUtils.isEmpty(autoId) && (field.getType().isAssignableFrom(Long.class))){
????????????????????????switch?(autoId.type()){
????????????????????????????case?SNOWFLAKE:
????????????????????????????????SnowFlakeAutoIdProcess snowFlakeAutoIdProcess = new?SnowFlakeAutoIdProcess(field);
????????????????????????????????snowFlakeAutoIdProcess.setPrimaryKey(autoId.primaryKey());
????????????????????????????????finalIdProcesses.add(snowFlakeAutoIdProcess);
????????????????????????????????break;
????????????????????????}
????????????????????}
????????????????});public?class?SnowFlakeAutoIdProcess?extends?BaseAutoIdProcess?{

????private?static?Snowflake snowflake = IdUtil.createSnowflake(0,0);


????public?SnowFlakeAutoIdProcess(Field field)?{
????????super(field);
????}

????@Override
????void?setFieldValue(Object entity)?throws?Exception{
????????long?value = snowflake.nextId();
????????field.set(entity,value);
????}
}

如果項(xiàng)目中的mapper.xml已經(jīng)的insert語(yǔ)句已經(jīng)含有id,比如

insert?into?sys_test( `id`,`type`, `url`,`menu_type`,`gmt_create`)values( #{id},#{type}, #{url},#{menuType},#{gmtCreate})

則只需到填充id值這一步。攔截器的任務(wù)就完成。如果mapper.xml的insert不含id,形如

insert?into?sys_test( `type`, `url`,`menu_type`,`gmt_create`)values( #{type}, #{url},#{menuType},#{gmtCreate})

則還需重寫insert語(yǔ)句以及新增id參數(shù)

重寫insert語(yǔ)句以及新增id參數(shù)(可選)a、重寫insert語(yǔ)句

方法一:
從 MappedStatement 對(duì)象中獲取 SqlSource 對(duì)象,再?gòu)膹?SqlSource 對(duì)象中獲取獲取 BoundSql 對(duì)象,通過 BoundSql#getSql 方法獲取原始的sql,最后在原始sql的基礎(chǔ)上追加id

方法二:

引入

<dependency>
??????<groupId>com.alibabagroupId>
??????<artifactId>druidartifactId>
??????<version>${druid.version}version>
????dependency>

通過

com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser

獲取相應(yīng)的表名、需要insert的字段名。然后重新拼湊出新的insert語(yǔ)句

b、把新的sql重置給Invocation

其核心實(shí)現(xiàn)思路是創(chuàng)建一個(gè)新的MappedStatement,新的MappedStatement綁定新sql,再把新的MappedStatement賦值給Invocation的args[0],代碼片段如下

private?void?resetSql2Invocation(Invocation invocation, BoundSqlHelper boundSqlHelper,Object entity)?throws?SQLException {
????????final?Object[] args = invocation.getArgs();
????????MappedStatement statement = (MappedStatement) args[0];
????????MappedStatement newStatement = newMappedStatement(statement, new?BoundSqlSqlSource(boundSqlHelper));
????????MetaObject msObject = MetaObject.forObject(newStatement, new?DefaultObjectFactory(), new?DefaultObjectWrapperFactory(),new?DefaultReflectorFactory());
????????msObject.setValue("sqlSource.boundSqlHelper.boundSql.sql", boundSqlHelper.getSql());

????????????args[0] = newStatement;

????}c、新增id參數(shù)

其核心是利用

org.apache.ibatis.mapping.ParameterMapping

核心代碼片段如下

private?void?setPrimaryKeyParaterMapping(String primaryKey) {
???????????ParameterMapping parameterMapping = new?ParameterMapping.Builder(boundSqlHelper.getConfiguration(),primaryKey,boundSqlHelper.getTypeHandler()).build();
???????????boundSqlHelper.getBoundSql().getParameterMappings().add(parameterMapping);
???????}d、將mybatis攔截器注入到spring容器

可以直接在攔截器上加

@org.springframework.stereotype.Component

注解。也可以通過

? ? @Bean
????public?AutoIdInterceptor autoIdInterceptor(){
????????return?new?AutoIdInterceptor();
????}e、在需要實(shí)現(xiàn)自增主鍵的實(shí)體字段上加如下注解?@AutoId(primaryKey?= "id")
??private Long id;

1

04

測(cè)試

對(duì)應(yīng)的測(cè)試實(shí)體以及單元測(cè)試代碼如下@Data
public?class?TestDO implements?Serializable {
??private?static?final long serialVersionUID = 1L;

??@AutoId(primaryKey = "id")
??private?Long id;
??private?Integer type;
??private?String?url;
??private?Date?gmtCreate;
??private?String?menuType;
}@Autowired
????private?TestService testService;

????@Test
????public?void?testAdd(){
????????TestDO testDO = new?TestDO();
????????testDO.setType(1);
????????testDO.setMenuType("1");
????????testDO.setUrl("www.test.com");
????????testDO.setGmtCreate(new?Date());
????????testService.save(testDO);
????????testService.get(110L);
????}

????@Test
????public?void?testBatch(){
????????List testDOList = new?ArrayList<>();for?(int?i = 0; i < 3; i++) {
????????????TestDO testDO = new?TestDO();
????????????testDO.setType(i);
????????????testDO.setMenuType(i+"");
????????????testDO.setUrl("www.test"+i+".com");
????????????testDO.setGmtCreate(new?Date());
????????????testDOList.add(testDO);
????????}
????????testService.saveBatch(testDOList);
????}當(dāng)mapper的insert語(yǔ)句中含有id

形如下

<insert?id="save"?parameterType="com.lybgeek.TestDO"?useGeneratedKeys="true"?keyProperty="id">
????insert?into sys_test(`id`,`type`, `url`,`menu_type`,`gmt_create`)
????values( #{id},#{type}, #{url},#{menuType},#{gmtCreate})
??insert>

以及批量插入sql

<insert?id="saveBatch"??parameterType="java.util.List"?useGeneratedKeys="false">
????insert?into sys_test( `id`,`gmt_create`,`type`,`url`,`menu_type`)
????values
????"list"?item="test"?index="index"?separator=",">
??????( #{test.id},#{test.gmtCreate},#{test.type}, #{test.url},
??????#{test.menuType})
??insert>

查看控制臺(tái)sql打印語(yǔ)句

15:52:04?[main] DEBUG com.lybgeek.dao.TestDao.save - ==> Preparing:?insert?into sys_test(`id`,`type`, `url`,`menu_type`,`gmt_create`) values( ?,?, ?,?,? )
15:52:04?[main] DEBUG com.lybgeek.dao.TestDao.save - ==> Parameters:?356829258376544258(Long), 1(Integer), www.test.com(String), 1(String), 2020-09-11?15:52:04.738(Timestamp)
15:52:04?[main] DEBUG com.lybgeek.dao.TestDao.save - <== Updates:?115:52:04?[main] DEBUG c.n.lybgeek.dao.TestDao.saveBatch - ==> Preparing: insert into sys_test( `id`,`gmt_create`,`type`,`url`,`menu_type`) values ( ?,?,?, ?, ?) , ( ?,?,?, ?, ?) , ( ?,?,?, ?, ?)
15:52:04?[main] DEBUG c.n.lybgeek.dao.TestDao.saveBatch - ==> Parameters: 356829258896637961(Long), 2020-09-11?15:52:04.847(Timestamp), 0(Integer), www.test0.com(String), 0(String), 356829258896637960(Long), 2020-09-11?15:52:04.847(Timestamp), 1(Integer), www.test1.com(String), 1(String), 356829258896637962(Long), 2020-09-11?15:52:04.847(Timestamp), 2(Integer), www.test2.com(String), 2(String)
15:52:04?[main] DEBUG c.n.lybgeek.dao.TestDao.saveBatch - <== Updates: 3

查看數(shù)據(jù)庫(kù)

當(dāng)mapper的insert語(yǔ)句中不含id

形如下

<insert?id="save"?parameterType="com.lybgeek.TestDO"?useGeneratedKeys="true"?keyProperty="id">
????insert?into sys_test(`type`, `url`,`menu_type`,`gmt_create`)
????values(#{type}, #{url},#{menuType},#{gmtCreate})
??insert>

以及批量插入sql

<insert?id="saveBatch"??parameterType="java.util.List"?useGeneratedKeys="false">
????insert?into sys_test(`gmt_create`,`type`,`url`,`menu_type`)
????values
????"list"?item="test"?index="index"?separator=",">
??????(#{test.gmtCreate},#{test.type}, #{test.url},
??????#{test.menuType})
??insert>

查看控制臺(tái)sql打印語(yǔ)句

15:59:46?[main] DEBUG com.lybgeek.dao.TestDao.save - ==> Preparing:?insert?into sys_test(`type`,`url`,`menu_type`,`gmt_create`,id) values?(?,?,?,?,?)
15:59:46?[main] DEBUG com.lybgeek.dao.TestDao.save - ==> Parameters:?1(Integer), www.test.com(String), 1(String), 2020-09-11?15:59:46.741(Timestamp), 356831196144992264(Long)
15:59:46?[main] DEBUG com.lybgeek.dao.TestDao.save - <== Updates:?115:59:46?[main] DEBUG c.n.lybgeek.dao.TestDao.saveBatch - ==> Preparing: insert into sys_test(`gmt_create`,`type`,`url`,`menu_type`,id) values (?,?,?,?,?),(?,?,?,?,?),(?,?,?,?,?)
15:59:46?[main] DEBUG c.n.lybgeek.dao.TestDao.saveBatch - ==> Parameters: 2020-09-11?15:59:46.845(Timestamp), 0(Integer), www.test0.com(String), 0(String), 356831196635725829(Long), 2020-09-11?15:59:46.845(Timestamp), 1(Integer), www.test1.com(String), 1(String), 356831196635725828(Long), 2020-09-11?15:59:46.845(Timestamp), 2(Integer), www.test2.com(String), 2(String), 356831196635725830(Long)
15:59:46?[main] DEBUG c.n.lybgeek.dao.TestDao.saveBatch - <== Updates: 3

從控制臺(tái)我們可以看出,當(dāng)mapper.xml沒有配置id字段時(shí),則攔截器會(huì)自動(dòng)幫我們追加id字段

查看數(shù)據(jù)庫(kù)

1

05

總結(jié)

本文雖然是介紹mybatis攔截器實(shí)現(xiàn)主鍵自動(dòng)生成,但文中更多講解如何實(shí)現(xiàn)一個(gè)攔截器以及主鍵生成思路,并沒把intercept實(shí)現(xiàn)主鍵方法貼出來。其原因主要是主鍵自動(dòng)生成在mybatis-plus里面就有實(shí)現(xiàn),其次是有思路后,大家就可以自己實(shí)現(xiàn)了。最后對(duì)具體實(shí)現(xiàn)感興趣的朋友,可以查看文末中demo鏈接

1

06

參考文檔?

https://www.cnblogs.com/chenchen127/p/12111159.html

https://blog.csdn.net/hncaoyuqi/article/details/103187983

https://blog.csdn.net/zsj777/article/details/81986096

1

07

demo鏈接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-mybatis-autoId

總結(jié)

以上是生活随笔為你收集整理的insert into select 主键自增_springboot2结合mybatis拦截器实现主键自动生成的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日一区二区 | 青青草av| 欧美久久一级 | 国产精品高清在线观看 | 99精品久久久久久久 | xx视频在线 | 一区二区在线影院 | 少妇大叫太粗太大爽一区二区 | 日本黄页网站 | 超碰神马| 蜜桃av一区| 日韩一级二级 | 黄色三级生活片 | 美女涩涩视频 | 国产成人精品电影 | 欧美整片第一页 | 国产对白刺激视频 | 国产精品无码av在线有声小说 | 绯色av一区二区三区高清 | 国产在线视频一区二区 | 亚洲精品666| 精品免费在线 | 女性向小h片资源在线观看 日本天天操 | 久久美女精品 | 少妇偷人精品无码人妻 | 日韩视频 中文字幕 | 日本黄色激情视频 | a级黄片毛片 | 在线看毛片网站 | 久久精品偷拍视频 | 折磨小男生性器羞耻的故事 | 欧美一级黄色片在线观看 | 欧美日韩亚洲在线 | 欧美 日韩 国产 激情 | 免费观看国产精品 | 黄色大片av| 日韩精品在线视频 | 欧美交换 | 老司机成人免费视频 | 天天摸夜夜添 | 欧美少妇xxx| 国产主播一区 | av直接看 | 欧美性生活网站 | 日韩欧美aaa| 日日天天干| 久久久福利视频 | 久久影院午夜 | 久久久久久www | 免费啪视频在线观看 | 天堂久久网 | 青青青视频在线 | 婷婷中文字幕在线 | 欧美深性狂猛ⅹxxx深喉 | 欧美中文字幕一区二区 | 国产黄色大片在线观看 | 黄网站在线免费 | 成人高清免费 | 九九这里只有精品视频 | 99re只有精品 | 91porny首页入口| 一区二区三区日韩欧美 | 亚洲激情视频在线观看 | 肉体粗喘娇吟国产91 | 日韩精品视频在线看 | 欧美黄色免费视频 | 天天摸天天做天天爽 | 熟妇人妻系列aⅴ无码专区友真希 | aa亚洲| 久久天堂视频 | 久操国产在线 | 性盈盈影院中文字幕 | 美女黄色片网站 | 国产成人综合亚洲 | 蜜芽在线视频 | 在线观看一区二区视频 | 欧美精品v国产精品v日韩精品 | 欧美精彩视频 | 动漫av在线 | 日本一区二区不卡在线观看 | 国产男女精品 | 东北少妇av | 国产精品精品久久久久久 | 黄瓜视频色版 | av毛片在线播放 | 五月天激情视频在线观看 | 国产不卡a | 欧美一级高潮片 | 可以看av| 丁香一区二区三区 | 4444亚洲人成无码网在线观看 | 日韩aaa | 免费黄av | 丰满少妇一区二区三区视频 | 国产中文一区二区三区 | 欧美在线视频你懂的 | www欧美精品 | 丁香花电影免费播放在线观看 | 亚洲电影一区二区 |