Springboot整合通用mapper
引言
我第一次整合orm框架的时候,本来使用jpa,多表联查比较麻烦(只是单纯的使用,没深入使用,可能有比较好的方法使用,我没用到),单纯使用mybatis,写单纯的crud的方法,感觉自己手写太麻烦,如果用mybatis的逆向工程,它又给你生成各种方法,我觉得很烦。那有没有一个框架可以简简单单的让我不需要太多的东西就可以使用单表的操作呢,我发现了通用mapper。(后来其实发现,mybatis-plus两个思路大同小异,都挺好的。)
一、导入依赖
<!-- 通用Mapper -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.1.5</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
<!-- mapper启动类,不知道要不要,但是运行没问题,估计没冲突 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.6</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- Mapper的逆向工程 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- web模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
第一部分核心依赖,第二部分是一些配套的,看你需求。我是用mybatis的逆向工程的
二、启动类添加注解
@MapperScan(value = "com.xyo.it.service.**.mapper")
注意,这个是import tk.mybatis.spring.annotation.MapperScan;扫描的你的mapper,注意通配符的使用
三、yaml
mybatis:
mapper-locations: classpath:mapping/**/*Mapper.xml
type-aliases-package: com.xyo.it.*.entity
这个应该是mybatis的注解,用于扫描xml的吧?
四、使用
前面都不是重点,重点在使用,帮我们做到了什么。
1. 继承通用的Mapper<T>
,必须指定泛型<T>
public interface RecordMapper extends BaseMapper<Record> {
}
一旦继承了Mapper<T>
,继承的Mapper
就拥有了Mapper<T>
所有的通用方法。
里面包含了几乎所有的单表的crud方法。而查询的话,基本使用的Example。
Example example = new Example(FiveClassRecord.class);
Example.Criteria criteria = example.or();
criteria.andEqualTo("taskNo", lcyhTaskNo);//注意,这个是类的属性,与mybatis不同,不是用数据库的字段名,是用的类的属性,
咋一看是没什么东西,但是我主要看上了他的组装功能
2.接口可以自定义搭配继承
如果你想按需选择接口,不想使用Mapper<T>
包含的那么多的方法,你可以创建自己的MyMapper<T>
,自己搭配想要的方法
主要场景在于,我可能之前用的是oracle数据库,然后我切换回db2数据库,突然发现,id自增,不需要指定id了,那怎么办
Mapper3接口有两种形式,一种是提供了一个方法的接口。还有一种是不提供方法,但是继承了多个单方法的接口,一般是某类方法的集合。
你可以按需的组装
//注意,如果是sqlserverMapper这种,因为insert不允许id包含为null,所以继承sqlServerMapper
//另外这俩方法和base中的插入方法重名,不能同时存在!所以拆分了
public interface BaseMapper<T> extends BaseSelectMapper<T>, BaseUpdateMapper<T>, BaseDeleteMapper<T>, ExampleMapper<T>, RowBoundsMapper<T>, SqlServerMapper<T>, MyBatchInsertMapper<T> {
}
我的基本mapper就是这样,组合了基本的crud,查询,分页。加上一个自定义的mapper,这个后续说。
主要问题在于,mybatis不是跟hibernate一样提供方言,所以,d自增,不需要指定id了,那么我们就可以用支持不带上id的插入。
3.支持逆向工程
public class GeneratorSqlmap {
public void generator() throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
// 指定配置文件
File configFile = new File("./src/main/resources/generatorConfig/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
// 执行main方法以生成代码
public static void main(String[] args) {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}
generatorConfig.xml
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers" value="com.platform.common.BaseMapper"/>
</plugin>
<commentGenerator>
<!-- 是否去除自动生成的注释 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!-- DB2数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.ibm.db2.jcc.DB2Driver"
connectionURL="jdbc:db2://66.10.93.82:60004/DFTCE:currentSchema=DFTCE;"
userId="dftce"
password="2vgrTNY51pK3hQ2e">
</jdbcConnection>
<!-- Oracle数据库
<jdbcConnection driverClass="oracle.jdbc.OracleDriver"
connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg"
userId="yycg"
password="yycg">
</jdbcConnection>
-->
<!-- 默认为false,把JDBC DECIMAL 和NUMERIC类型解析为Integer,为true时
把JDBC DECIMAL 和NUMERIC类型解析为java.math.BigDecimal -->
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成POJO类的位置 -->
<javaModelGenerator targetPackage="com.beawan.exterinvoke.out.newhfw.entity" targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="mapping.newhfw" targetProject=".\src\main\resources">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetProject:mapper接口生成的的位置 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.beawan.exterinvoke.out.newhfw.mapper" targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- <table schema="" tableName="QC_MODEL" domainObjectName="QcModel"
enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false">
<generatedKey column="id" sqlStatement="JDBC"/>
<columnOverride column="ID" javaType="java.lang.Long"></columnOverride>
</table>-->
<table schema="DFTCE" tableName="COM_EXECUTE_INFO" domainObjectName="comExecuteInfo"
enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" >
<generatedKey column="ID" sqlStatement="JDBC"/>
<columnOverride column="ID" javaType="java.lang.Long"/>
<columnOverride column="C_CASE_CODE" property="C_CASE_CODE" />
<columnOverride column="C_INAME" property="C_INAME" />
<columnOverride column="N_CARD_TYPE" property="N_CARD_TYPE" />
<columnOverride column="C_CARD_NUM" property="C_CARD_NUM" />
<columnOverride column="C_GIST_CID" property="C_GIST_CID" />
<columnOverride column="APPLY_PARTY" property="APPLY_PARTY" />
<columnOverride column="EXECUTED_PARTY" property="EXECUTED_PARTY" />
<columnOverride column="C_COURT_NAME" property="C_COURT_NAME" />
<columnOverride column="C_PARTY_TYPE" property="C_PARTY_TYPE" />
<columnOverride column="C_BUEINESS_ENTITY_NAME" property="C_BUEINESS_ENTITY_NAME" />
<columnOverride column="C_ADDRESS" property="C_ADDRESS" />
<columnOverride column="C_PUBLISH_DATE" property="C_PUBLISH_DATE" />
<columnOverride column="C_REG_DATE" property="C_REG_DATE" />
<columnOverride column="N_APPLY_MONEY" property="N_APPLY_MONEY" />
<columnOverride column="N_EXECUTED_MONEY" property="N_EXECUTED_MONEY" />
<columnOverride column="C_EFFECTIVE_FLAG" property="C_EFFECTIVE_FLAG" />
<columnOverride column="D_DATA_DATE" property="D_DATA_DATE" />
</table>
</context>
</generatorConfiguration>
这里我指定了mybatis的插件为tkmapper的,然后让他mapper继承我的通用mapper,同时不让生成example类。
那么会生成什么呢?
就两个,一个entity类,一个mapper。
entity
@Table(name = "AC_CONFIGURATION")
public class AcConfiguration {
@Id
@Column(name = "ID")
@GeneratedValue(generator = "JDBC")
private Long id;
@Column(name = "ASSESS_CARD_ID")
private Long assessCardId;
@Column(name = "STATE")
private String state;
@Column(name = "PASS_SCORE")
private Double passScore;
@Column(name = "REJECT_SCORE")
private Double rejectScore;
@Column(name = "FINANCE_RATION")
private Double financeRation;
@Column(name = "UNFINANCE_RATIO")
private Double unfinanceRatio;
@Column(name = "SCORE_WAY")
private String scoreWay;
@Column(name = "LOAN_TYPE")
private String loanType;
@Column(name = "GUARANTEE_TYPE")
private String guaranteeType;
@Column(name = "ENTERPRISE_TYPE")
private String enterpriseType;
@Column(name = "CARD_TYPE")
private String cardType;
@Column(name = "SUGG_SCORE")
private Double suggScore;
@Column(name = "AC_CUS_LEVEL_SET")
private String acCusLevelSet;
/**
* 非持久化字段
*/
private List<AcCusLevel> acCusLevelList;
/**
* @return ID
*/
public Long getId() {
return id;
}
/**
* @param id
*/
public void setId(Long id) {
this.id = id;
}
public List<AcCusLevel> getAcCusLevelList() {
return acCusLevelList;
}
public void setAcCusLevelList(List<AcCusLevel> acCusLevelList) {
this.acCusLevelList = acCusLevelList;
}
/**
* @return ASSESS_CARD_ID
*/
public Long getAssessCardId() {
return assessCardId;
}
/**
* @param assessCardId
*/
public void setAssessCardId(Long assessCardId) {
this.assessCardId = assessCardId;
}
/**
* @return STATE
*/
public String getState() {
return state;
}
/**
* @param state
*/
public void setState(String state) {
this.state = state == null ? null : state.trim();
}
/**
* @return PASS_SCORE
*/
public Double getPassScore() {
return passScore;
}
看出来了没有,就是用的jpa的注解
所以切换的成本很低
非持久化字段,可以加 @Transient
注解
而mapper就继承了我的通用mapper,比mybatis的逆向工程生成简洁了很多。
4.支持开发自己的mapper
我还是看上了这个功能,开发自己的mapper,diy很开心
比如我们要开发一个批量插入功能,那么怎么做
定义批量接口
@RegisterMapper
public interface MyBatchInsertMapper<T> {
@Options(useGeneratedKeys = true,keyProperty = "id")
@InsertProvider(type = MyBatchInsertProvider.class,method = "dynamicSQL")
int batchInsert(List<T> recoordList);
}
实现自定义的类MyBatchInsertProvider.class
public class MyBatchInsertProvider extends MapperTemplate {
public MyBatchInsertProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
public String batchInsert(MappedStatement ms){
final Class<?> entiyClass = this.getEntityClass(ms);
EntityTable table = EntityHelper.getEntityTable(entiyClass);
LinkedHashSet<EntityColumn> tableColumns = table.getEntityClassColumns();
LinkedHashSet<EntityColumn> nopkColumns = getNoPKColumn(tableColumns);
StringBuilder sql = new StringBuilder();
sql.append("insert into ");
sql.append(table.getName());
sql.append("(");
boolean first = true;
for(EntityColumn column :nopkColumns){
if(!first){
sql.append(",");
}
sql.append(column.getColumn());
first=false;
}
sql.append(") values ");
sql.append("<foreach collection=\"list\" item=\"record\" separator=\",\">");
sql.append("(");
first=true;
for(EntityColumn column : nopkColumns){
if(!first){
sql.append(",");
}
sql.append("#{record.").append(column.getProperty()).append("}");
first=false;
}
sql.append(")");
sql.append("</foreach>");
return sql.toString();
}
public static LinkedHashSet<EntityColumn> getNoPKColumn( LinkedHashSet<EntityColumn> tableColumns){
LinkedHashSet<EntityColumn> noPKColumn =new LinkedHashSet<>();
if(CollectionUtil.isNullOrEmpty(tableColumns)) return null;
for(EntityColumn entityColumn:tableColumns){
if(!"id".equals(entityColumn.getColumn().toLowerCase()) ){
noPKColumn.add(entityColumn);
}
}
return noPKColumn;
}
}
其实思路就是凭借mybatis格式的xml
这样我们就实现了批量插入,支持的是不带上id的批量插入
结语
通用Mapper都可以极大的方便开发人员。可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法。
极其方便的使用MyBatis单表的增删改查。
支持单表操作,不支持通用的多表联合查询。
其实这样还是比较简单的使用,我的使用方法是先在数据库设计生成好表,加好注释,然后运行逆向工程,生成了entity和mapper和xml,然后我再运行我自己写的自动生成 模板的servcie接口和实现类,以及controller,这样一个完整的后台的单表查询接口就写完了,我只需要设计表就好,其他都只是运行下代码就好。
toLowerCase()) ){
noPKColumn.add(entityColumn);
}
}
return noPKColumn;
}
}
其实思路就是凭借mybatis格式的xml
这样我们就实现了批量插入,支持的是不带上id的批量插入
## 结语
通用Mapper都可以极大的方便开发人员。可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法。
极其方便的使用MyBatis单表的增删改查。
支持单表操作,不支持通用的多表联合查询。
其实这样还是比较简单的使用,我的使用方法是先在数据库设计生成好表,加好注释,然后运行逆向工程,生成了entity和mapper和xml,然后我再运行我自己写的自动生成 模板的servcie接口和实现类,以及controller,这样一个完整的后台的单表查询接口就写完了,我只需要设计表就好,其他都只是运行下代码就好。
大部分市面上的单表接口,也是这样的思路生成的,不过不同的是,我加上了自己的逻辑,写了自己的模板。