tk.mybatis通用工具pom
<!--mybatis依賴-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!--通用mapper-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>1.1.5</version>
</dependency>
- 我使用springboot搭建,所以引用的都是springboot封裝的組件
采坑點
批量插入數據,默認主鍵只支持id
- 繼承此工具MySqlMapper方法后
/**
* 通用Mapper接口,MySql獨有的通用方法
*
* @param <T> 不能為空
* @author liuzh
*/
public interface MySqlMapper<T> extends
InsertListMapper<T>,
InsertUseGeneratedKeysMapper<T> {
}
- 可以看到存在可以批量插入類到數據庫的方法
- 如果項目中途加入了此工具,那么恭喜,估計要踩很多坑~
- 數據庫表主鍵不為id時,可以繼續看源碼
public interface InsertListMapper<T> {
/**
* 批量插入,支持批量插入的數據庫可以使用,例如MySQL,H2等,另外該接口限制實體包含`id`屬性並且必須為自增列
*
* @param recordList
* @return
*/
@Options(useGeneratedKeys = true, keyProperty = "id")
@InsertProvider(type = SpecialProvider.class, method = "dynamicSQL")
int insertList(List<T> recordList);
/**
* ======如果主鍵不是id怎么用?==========
* 假設主鍵的屬性名是uid,那么新建一個Mapper接口如下
* <pre>
public interface InsertUidListMapper<T> {
@Options(useGeneratedKeys = true, keyProperty = "uid")
@InsertProvider(type = SpecialProvider.class, method = "dynamicSQL")
int insertList(List<T> recordList);
}
* 只要修改keyProperty = "uid"就可以
*
* 然后讓你自己的Mapper繼承InsertUidListMapper<T>即可
*
* </pre>
*/
}
- 他留着坑還是很負責的把解決辦法留下了~~
- 我是按網上通用的搭建方式搭建的目錄結構,也就是如下結構
/**
* 繼承自己的MyMapper
*
*/
public interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {
//FIXME 特別注意,該接口不能被掃描到,否則會出錯
//FIXME 最后在啟動類中通過MapperScan注解指定掃描的mapper路徑:
}
- 因為InsertUidListMapper這種特殊寫的mapper和MyMapper是同一類,如果不同樣配置下會報如下錯誤
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: Error invoking SqlProvider method (tk.mybatis.mapper.provider.SpecialProvider.dynamicSQL). Cause: java.lang.InstantiationException: tk.mybatis.mapper.provider.SpecialProvider
- 這時候你要,在application.properties中,把InsertUidListMapper路徑配上去
mapper.mappers=com.tzxylao.manager.utils.MyMapper,com.tzxylao.manager.mapper_ext.InsertUidListMapper
- 還有一點,光這樣匹配后還不夠,自己寫的類DpageConfigMapper,繼承InsertUidListMapper
這個類后,把DpageConfigMapper類放在Mapper目錄下,那么又會很糾結。系統啟動會掃描不到DpageConfigMapper這個類。。我竟然找不到默認掃描Mapper包的地方。。但是經驗告訴我,繼承了MyMapper的類都能被掃描到~~ - 可是自己的類繼承的是InsertUidListMapper
,想要它被掃描,啟動類就要加上掃描包的地方。。我掃描了整個Mapper包
@EnableTransactionManagement // 啟注解事務管理,等同於xml配置方式的
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableAutoConfiguration
@MapperScan("com.tzxylao.manager.mapper")
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
- 但這樣要注意,繼承類InsertUidListMapper
,不能再這個包里,否則會報該類不能被掃描錯誤~~ - 就算把這個類移到了其他包,系統也可以正常運行了。。其實還是有點問題。。啟動的時候會出現類似的警告
Skipping MapperFactoryBean with name 'sysMenuMapper' and 'com.telchina.framework.sys.mapper.SysMenuMapper' mapperInterface. Bean already defined with the same name!
雖然不影響使用,但還是看着煩,他的意思就是這個類被掃了兩遍我就說哪里MyMapper類繼承的類都被莫名其妙掃了一遍。。自己再配置掃一遍就重復了
- 解決辦法就是再建個子包,不繼承MyMapper的自定義Mapper都放子包,再單獨配置子包掃描
@EnableTransactionManagement // 啟注解事務管理,等同於xml配置方式的
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableAutoConfiguration
@MapperScan("com.tzxylao.manager.mapper.ext")
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
把DpageConfigMapper類放ext子包里就行了。。
- 上面步驟都解決了,插入還是不成功的,插入語句沒有主鍵,自己生成的主鍵插不進去?
- 看源碼
/**
* 批量插入
*
* @param ms
*/
public String insertList(MappedStatement ms) {
final Class<?> entityClass = getEntityClass(ms);
//開始拼sql
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.insertIntoTable(entityClass, tableName(entityClass)));
sql.append(SqlHelper.insertColumns(entityClass, true, false, false));
sql.append(" VALUES ");
sql.append("<foreach collection=\"list\" item=\"record\" separator=\",\" >");
sql.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
//獲取全部列
Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
//當某個列有主鍵策略時,不需要考慮他的屬性是否為空,因為如果為空,一定會根據主鍵策略給他生成一個值
for (EntityColumn column : columnList) {
if (!column.isId() && column.isInsertable()) {
sql.append(column.getColumnHolder("record") + ",");
}
}
sql.append("</trim>");
sql.append("</foreach>");
return sql.toString();
}
- 可以看到,主鍵插入的地方,他有這樣的判斷!column.isId() && column.isInsertable(),所以,Bean類主鍵屬性上@Id去掉,就可以自己添加主鍵值了
總結
- 總覺的我的解決方法很low,限制也太大~有熟悉tk.mybatis的大神講下別的思路嗎?