1、簡單認識通用mapper
1.1、了解mapper
作用:就是為了幫助我們自動的生成sql語句
ps:MyBatis需要編寫xxxMapper.xml,而逆向工程是根據entity實體類來進行生成的,有時由於業務需要,會讓實體類與數據庫字段名不對應,所以逆向工程生成的xxxMapper.xml配置就會有問題。其實:通用Mapper和JPA很像
通用mapper是MyBatis的一個插件,是pageHelper的同一個作者進行開發的
作者gitee地址: https://gitee.com/free
通用mapper官網地址: https://gitee.com/free/Mapper
通用mapper文檔介紹地址: https://gitee.com/free/Mapper/wikis/Home
1.2、學習通用mapper需要的知識
Mybatis
Spring
2、玩通用mapper
2.1、准備工作
建測試表
CREATE TABLE `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用戶id',
`user_name` varchar(20) DEFAULT NULL COMMENT '用戶名',
`user_sex` varchar(2) DEFAULT NULL COMMENT '用戶性別',
`user_salary` decimal(5,2) DEFAULT NULL COMMENT '用戶薪資',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用戶表';
insert into `user`(`user_id`,`user_name`,`user_sex`,`user_salary`) values
(1,'紫邪情','女',100.00),
(2,'紫玲','女',50.00),
(3,'張三','男',999.99);
創建Spring項目 並 導入依賴
<!-- spring整合mybatis的依賴 -->
<!-- 1、spring需要的依賴 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!-- 2、mybatis的依賴 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- spring整合mybatis的第三方依賴 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!-- 1、數據庫驅動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- 通用mapper的依賴 -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>3.4.2</version>
</dependency>
</dependencies>
編寫SSM框架整合的xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- 1、獲取數據源 —— 使用druid -->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${druid.driverClassName}"/>
<property name="url" value="${druid.url}"/>
<property name="username" value="${druid.username}"/>
<property name="password" value="${druid.password}"/>
</bean>
<!-- 2、獲取SQLSessionFactory工廠-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 把mybatis集成進來 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/> <!-- 集成mybatis-config.xml -->
</bean>
<!-- 3、配置事務管理 -->
<!-- 聲明事務托管 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 說明哪些方法要進行事務托管 即:通知類 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<!-- 編寫切面 -->
<aop:config>
<!-- 切點 -->
<aop:pointcut id="pointCut" expression="execution(* cn.xiegongzi.mapper.*.*(..))"/>
<!-- 組裝切面 切點和通知類組裝 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut"/>
</aop:config>
<!-- 4、掃描mapper層,整合通用mapper的唯一一個注意點
原始SSM整合寫法是:org.mybatis.spring.mapper.MapperScannerConfigurer
現在用通用mapper替換:tk.mybatis.spring.mapper.MapperScannerConfigurer
為什么通用mapper可以替換掉mybatis?
因為:通用mapper的MapperScannerConfigurer在底層繼承了mybatis的MapperScannerConfigurer,可以點源碼
-->
<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.zixieqing.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<!-- 掃描service層 -->
<context:component-scan base-package="cn.zixieqing.service"/>
</beans>
注意點:在掃描mapper層時,使用通用mapper覆蓋mybatis,寫法不太一樣
我的項目結構如下
建對應的實體類
注意點:數據類型用包裝類,因為包裝類可以判斷null值,這個涉及到通用mapper的原理,數據類型用包裝類在MaBatis中就已經知道的事情
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class UserEntity implements Serializable {
private Integer userId;
private String userName;
private String userSex;
private Double userSalary;
}
2.2、玩通用mapper的基礎API
先看一下通用mapper大概有哪些API
// 這里接口直接繼承通用mapper接口即可
// 注意:泛型中的信息就是實體類
public interface UserMapper extends Mapper<UserEntity> { // 看源碼,點mapper即可進入
}
看看BaseMapper
其他的都是差不多的套路,歸納起來其實就是增刪查改的封裝,然后做了不同的細分,需要繼續查看的,那就往后挨個去點擊
2.2.1、和select相關
2.2.1、selectOne方法 和 @Table注解 及@Column注解
編寫測試類 並啟動
package cn.zixieqing;
import cn.zixieqing.entity.*;
import cn.zixieqing.mapper.*;
import org.junit.*;
import org.springframework.context.*;
import org.springframework.context.support.*;
public class MyTest {
@Test
public void selectOneTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
UserEntity userEntity = new UserEntity();
userEntity.setUserId(1)
.setUserName("紫邪情")
.setUserSex("女")
.setUserSalary(new Double(100));
System.out.println(userMapper.selectOne(userEntity));
}
}
出現報錯:Table 'mapper_study.user_entity' doesn't exist
原因就是編寫的實體類名叫做UserEntity
,而數據庫的表名叫做user
,解決方式就是在實體類中加入@Table
注釋,注意此注解是import javax.persistence.*;
包下的
另外的selectxxx方法直接一點就出來了,基本上都是見名知意,就算不知道的源碼中也有解釋,通用mapper就是國內人寫的
至於@Column注解就是見名知意,用來處理實體類的字段和數據庫中的字段不一致的問題
- 默認規則:
- 實體類字段:駝峰式命名
- 數據庫表字段:使用“_”區分各個單詞
2.2.2、觀察日志總結selectOne方法
selectOne()是將封裝的實體類作為了WHERE子句的條件
- 這里是使用了非空的值作為的WHERE子句
在條件表達式中使用“=”進行比較
注意點:要求必須返回一個實體類結果,如果有多個,則會拋出異常
2.2.3、xxxByPrimaryKey方法 和 @Id注解
測試
@Test
public void selectByPrimaryKey() {
System.out.println(userMapper.selectByPrimaryKey(3));
}
結果:發現將實體類的所有字段屬性都作為WHERE子句的條件了
解決辦法:給實體類中對應的數據庫表的主鍵字段加上@Id注解
2.2.4、xxxByPrimaryKey方法 和 @Id注解總結
@Id注解
為了將實體類字段屬性和對應的數據庫表主鍵做匹配
原因:通用mapper在執行xxxByPrimaryKey方法時會出現兩種情況:
- 1、沒有加@Id注解時,通用mapper會將實體類的所有屬性作為聯合主鍵來匹配數據庫表的主鍵,故而會出現將實體類中的字段屬性全部作為WHERE子句后面的條件字段
SELECT user_id,user_name,user_sex,user_salary
FROM user
WHERE user_id = ? AND user_name = ? AND user_sex = ? AND user_salary = ?
- 2、使用@Id主鍵將實體類中的字段屬性和數據庫表中的主鍵做明確匹配
xxxByPrimaryKey方法
需要使用@Id注解來讓實體類中的字段屬性和數據庫表中的主鍵做明確匹配,否則:通用mapper默認將實體類的所有字段屬性作為聯合主鍵來進行匹配
2.2.5、select方法
傳什么,就用什么來拼接WHERE子句的條件
測試
@Test
public void select() {
UserEntity userEntity = new UserEntity();
userEntity.setUserName("紫邪情");
System.out.println(userMapper.select(userEntity));
}
2.2.6、xxxSelective方法
可選擇的嘛
非主鍵字段,如果不為null,則就加入sql語句
注意:是非null啊,所以前面才說實體類的類型最好用包裝類
2.2.2、和insert相關
2.2.2.1、insert方法
測試
@Test
public void insertTest() {
UserEntity userEntity = new UserEntity();
userEntity.setUserId(4)
.setUserName("不知火舞");
// 這個API會將null也拼接到SQL語句中
System.out.println(userMapper.insert(userEntity));
}
2.2.2.2、insertSelective方法
@Test
public void insertSelective() {
UserEntity userEntity = new UserEntity();
userEntity.setUserName("百里守約")
.setSex("老六");
// 這個API會將非null的字段拼接sql語句中
userMapper.insertSelective(userEntity);
}
2.2.2.3、@GeneratedValue注解
這個注解是為了讓通用mapper在執行insert語句之后,把數據庫中自動生成的主鍵值回填到實體類對象中
官網文檔介紹:https://gitee.com/free/Mapper/wikis/2.orm/2.3-generatedvalue
2.2.3、和update相關
2.2.3.1、updateByPrimaryKeySelective方法
這個其實看一眼就知道了,也就是:根據主鍵把不為null值的字段修改掉,即:set后面的字段就是實體類中不為null的字段
@Test
public void updateByPrimaryKeySelectiveTest() {
System.out.println( userMapper.updateByPrimaryKeySelective(new UserEntity().setUserId(1).setUserName("小紫")));
}
2.2.4、和delete相關
2.2.4.1、delete方法
切記:使用時,記得把實體類值傳進去,否則:若是null的實體類,則:SQL語句就沒有WHERE條件了,繼而:變成全表的邏輯刪除了
原理:還是一樣的,使用非null的字段作為WHERE子句條件
2.2.4.2、deleteByPrimaryKey方法
見名知意,直接通過主鍵來刪
@Test
public void deleteByPrimaryKeyTest() {
userMapper.deleteByPrimaryKey(2);
}
2.2.5、@Transient注解
一般情況下,實體中的字段和數據庫表中的字段是一一對應的,但是也有很多情況我們會在實體中增加一些額外的屬性,這種情況下,就需要使用 @Transient
注解來告訴通用 Mapper 這不是表中的字段
@Transient
private String otherThings; //非數據庫表中字段
2.3、QBC查詢
QBC全稱:query by criteria 也就是通過規則( criteria )來查詢
2.3.1、Criteria對象
public class ExampleTest {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
// 1、創建Example對象
Example example = new Example(UserEntity.class);
@Test
public void exampleTest() {
// 2、使用Example創建Criteria對象
Example.Criteria criteria = example.createCriteria();
// 3、添加規則 下面這些都是Criteria能夠調的API,還有其他的,需要時再說
/*
andGreaterThan 即:> andGreaterThanOrEqualTo 即:>=
andLessThan 即:< andLessThanOrEqualTo 即:<=
andIn 即:就是SQL的in andNotIn 就是SQL的not in
andBetween 即:SQL的between andNotBetween 即SQL中的not between
andLike 即sql中的like andNotLike 即SQL的not like
要看這些比較符,直接點andGreaterThan看源碼,里面都有各種介紹
*/
criteria.andGreaterThan("userSalary", 50).andLessThan("userSalary", 200);
// 4、調用Example封裝的API 不止selectByExample這一個,還有其他的CRUD
List<UserEntity> userEntities = userMapper.selectByExample(example);
for (UserEntity userEntity : userEntities) {
System.out.println(userEntity);
}
}
}
2.3.2、Example對象能調用的API
2.3.2.1、CreateCriteria()
@Test
public void createCriteriaTest() {
// createCriteria創建規則 - 有一個別扭的注意點
Example.Criteria criteria01 = example.createCriteria();
Example.Criteria criteria02 = example.createCriteria();
// 使用Example調用or()時,傳入的這個Criteria對象的參數有點別扭
// 添加規則1
criteria01.andGreaterThan("userId", 1).andLessThan("userId", 6);
// 添加規則2
criteria02.andGreaterThan("userSalary", 100).andLessThan("userSalary", 500);
/*
* 拼接的SQL語句:
* SELECT user_id,user_name,user_sex,user_salary
* FROM user
* WHERE ( user_id > ? and user_id < ? ) or ( user_salary > ? and user_salary < ? )
* */
// 別扭之處就在這里:是將規則2 criteria02 使用or拼接起來,理論上應該是criteria01.or(criteria02)
// 但是:卻是使用example來調的or( Criteria criteria ),所以感覺example就相當於是criteria01一樣,有點別扭
example.or(criteria02);
List<UserEntity> userEntities = userMapper.selectByExample(example);
userEntities.forEach(System.out::println);
}
2.3.2.2、orderBy( String property )排序
@Test
public void orderByTest() {
// userSalary 排序字段 desc 排序方式 - 降序desc 升序asc
example.orderBy("userSalary").desc();
userMapper.selectByExample(example).forEach(System.out::println);
}
2.3.2.3、setDistinct( boolean isDistinct )去重
@Test
public void setDistinctTest() {
example.setDistinct(true);
userMapper.selectByExample(example).forEach(System.out::println);
}
2.3.2.4、selectProperties( String... properties )設置select后的字段
/**
* 設置拼接SQL的select后面的字段
*/
@Test
public void selectPropertiesTest() {
// 拼接的SQL語句: SELECT user_id , user_name FROM user
// 默認是* 即:實體類的所有字段都拼接上去了
example.selectProperties("userId","userName");
userMapper.selectByExample(example).forEach(System.out::println);
}
2.3.2.5、excludeProperties(String... properties)設置select后不包含的字段
/**
* 設置select后不包含的字段
*/
@Test
public void excludePropertiesTest() {
// SQL語句 SELECT user_name , user_sex FROM user
example.excludeProperties("userId", "userSalary");
userMapper.selectByExample(example).forEach(System.out::println);
}
其他的API直接用example點就出來了,都是見名知意的
2.4、通用mapper逆向工程
2.4.1、pom.xml配置
<!-- 注意:別少了這個依賴啊,下面plugins中的依賴,那只是插件需要的依賴
沒有引入這個dependency的通用mapper依賴的話,那么生成的代碼需要引入一些包,到時就是不存在,會報錯的
-->
<dependencies>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.0.0-beta3</version>
</dependency>
<!-- 有些人可能會出現生成的mapper接口層報錯,說的是:rowBounds不存在 查看import發現源碼是引入的org.mybatis
但是目前我用得時候並沒有報錯,所以:為了以防萬一還是加上這個org.mybatis依賴
-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<!-- generatorConfig.xml逆向工程配置文件所在地 根據需要自行修改 -->
<configurationFile>
${basedir}/generatorConfig.xml
</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
<!-- 通用mapper逆向工程需要的兩個依賴 -->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.0.0-beta3</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
若是出現上述報RowBounds不存在的原因在下面這里
2.4.2、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>
<!-- 引入外部數據庫配置文件 最好使用引入,否則:下面數據庫配置那里奇葩要求很多 -->
<properties resource="db.properties"/>
<!-- MySQL基礎信息 就是起始和結束分隔符 如:`` -->
<context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<!-- 通用mapper插件 -->
<!--
type 是通用mapper插件,這個可以配置在前面引入的那個外部配置文件中,即配置成如下:
mapper.plugin=tk.mybatis.mapper.generator.MapperPlugin
然后在這里使用${mapper.plugin}引入,這種方式方便管理
-->
<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<!-- 這是mapper接口層中extend繼承的那個類,即:public interface userMapper extends Mapper<User> -->
<property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
<!-- 這是區別大小寫 如:user 和 User -->
<property name="caseSensitive" value="true"/>
</plugin>
<!-- 數據庫 -->
<jdbcConnection driverClass="${jdbc.driver}"
connectionURL="${jdbc.url}"
userId="${jdbc.username}"
password="${jdbc.password}">
</jdbcConnection>
<!-- 實體類 -->
<javaModelGenerator targetPackage="cn.zixieqing.entity"
targetProject="src/main/java"/>
<!-- xxxMapper.xml所在位置 -->
<sqlMapGenerator targetPackage="mapper"
targetProject="src/main/resources"/>
<!-- mapper接口層 -->
<javaClientGenerator targetPackage="cn.zixieqing.mapper"
targetProject="src/main/java"
type="XMLMAPPER"/>
<!-- 數據庫表名 和 實體類生成關系
如果感覺每個表都配置麻煩,那么直接改變tableName的值即可,即:tableName="%"
但是:此種方式的默認規則是采用 _ 轉駝峰命名,如:table_name ——> TableName
可是:有時我們並不需要這樣命名,此時就需要使用tableName 和 domainObjectName兩個配置項一起來配置
tableName 數據庫表名
domainObjectName 生成的實體類名
-->
<table tableName="user" domainObjectName = "User">
<!-- 主鍵生成策略 -->
<generatedKey column="user_id" sqlStatement="JDBC"/>
</table>
</context>
</generatorConfiguration>
引入的外部文件db.properties的配置
# 數據庫配置
jdbc.driver=com.mysql.jdbc.Driver
# 注意:建議使用db.properties配置從而在generatorConfig.xml中引入的原因就在這里
# 在這里可以在這個url后面拼接參數,如:useSSL=false
# 若是直接把這些配置寫到generatorConfig.xml中,那么后面的參數配置就有幾個奇葩的地方
# 1、參數之間不是通過&隔開,而是需要使用;分號隔開 如:useSSL=false;useUnicode=true
# 2、false / true等值需要使用``括起來,具體可以嘗試,然后看報的ERROR
jdbc.url=jdbc:mysql://localhost:3306/mapper_study?useSSL=false&useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
#c3p0
jdbc.maxPoolSize=50
jdbc.minPoolSize=10
jdbc.maxStatements=100
jdbc.testConnection=true
# 通用Mapper配置 若是在generatorConfig.xml的plugin配置中是通過引入的方式來做的,那么就可以在這里配置這兩個信息
# 從而方便管理
# mapper.plugin=tk.mybatis.mapper.generator.MapperPlugin
# mapper.Mapper=tk.mybatis.mapper.common.Mapper
2.4.3、啟動
在 pom.xml 這一級目錄 的命令行窗口執行 mvn mybatis-generator:generate
即可
2.5、自定義mapper
自定義mapper接口的作用: 根據實際需要自行重組mapper接口
ps:即 並不是通用mapper中的所有接口和方法都需要
2.5.1、玩一下自定義mapper接口
1、自定義自己要的mapper接口
package cn.zixieqing.common;
import tk.mybatis.mapper.common.*;
public interface CustomMapper<T> extends BaseMapper<T> {
// 這個自定義的mapper,想繼承前面畫的通用mapper中的哪個接口都可以
}
2、編寫業務mapper
package cn.zixieqing.mapper;
import cn.zixieqing.common.*;
import cn.zixieqing.entity.*;
public interface UserMapper extends CustomMapper<UserEntity> {
}
注意點:別把自定義mapper和業務mapper放到一個包中,會報錯
3、修改applicationContext.xml文件的MapperScannerConfigurer配置
<!-- 4、掃描mapper層,整合通用mapper的唯一一個注意點
原始SSM整合寫法是:org.mybatis.spring.mapper.MapperScannerConfigurer
現在用通用mapper替換:tk.mybatis.spring.mapper.MapperScannerConfigurer
為什么通用mapper可以替換掉mybatis?
因為:通用mapper的MapperScannerConfigurer在底層繼承了mybatis的MapperScannerConfigurer,可以點源碼
-->
<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.zixieqing.mapper"/>
<!--<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>-->
<property name="properties">
<value>
<!--自定義接口所在的包路徑-->
mapper=cn.zixieqing.common.CustomMapper
</value>
</property>
</bean>
4、測試
@Test
public void customMapperTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper",UserMapper.class);
userMapper.selectAll().forEach(System.out::println);
}
補充1、要是不想寫applicationContext.xml中MapperScannerConfigurer的那個配置
那把內容注釋掉,在自定義mapper接口的地方加個注解@RegisterMapper就搞定了
補充2、如果將自定義mapper接口 和 業務mapper接口放到一個包中了
一運行就會報錯
tk.mybatis.mapper.MapperException: java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
原因就是利用反射,獲取類對象時失敗,即:原因如下
2.6、了解通用mapper的二級緩存
一二級緩存的概念哪些就跳過了,MyBatis中已經見過了,這里玩通用mapper的配置
測試:自行編寫一個業務mapper,然后去繼承mapper<T>
,從而多次執行,會發現SQL執行了多次
注意:需要讓mapper<T>
中的實體類T實現Serializable接口,從而讓其擁用序列號,否則:會報錯的
修改mybatis-config.xml文件
<settings>
<!--顯示開啟緩存-->
<setting name="cacheEnabled" value="true"/>
</settings>
在業務mapper接口中添加@CacheNamespace注解
@CacheNamespace
public interface UserMapper extends Mapper<UserEntity> {
}
再次測試
2.7、類型處理器 typeHandler
這里說的類型是簡單類型和復雜類型,注意:和Java中說的基本類型和引用類型不是一回事,不是說基本類型就一定是簡單類型,這里不用去考慮基本和引用的問題
簡單類型和復雜類型可以參考一對一和一對多這兩種
簡單類型: 只有一個值
復雜類型: 有多個值
而上面這種,對於userName來說,是無法進行CRUD的
這種情況就是復雜類型,而通用mapper默認是沒處理的,就有點類似於在上述例子的userName上加了一個@Transient注解,從而忽略了該字段,從而造成的效果就是:去數據庫中找對應的字段值時沒找到,從數據庫中找到數據,然后返還給對象時沒有相應的對象可以接受
解決辦法:自定義類型處理器
具體操作流程如下:
1. 創建一個類型處理器的類,然后實現TypeHandler<T>
接口,其中:T就是要處理的那個類型,如:上述例子的NameEntity
2. 實現里面的四個方法
@Override
public void setParameter(PreparedStatement preparedStatement, int i, NameEntity nameEntity, JdbcType jdbcType) throws SQLException {
}
@Override
public NameEntity getResult(ResultSet resultSet, String s) throws SQLException {
return null;
}
@Override
public NameEntity getResult(ResultSet resultSet, int i) throws SQLException {
return null;
}
@Override
public NameEntity getResult(CallableStatement callableStatement, int i) throws SQLException {
return null;
}
實例邏輯編寫如下
public class NameHandler implements TypeHandler<NameEntity> {
/**
* 這個方法就是:對象NameEntity ——> 數據庫的流程規則,可以將其理解為序列化流程 但是完全不一樣啊
* 只是說:像序列化一樣把數據轉成一個樣
* @param ps
* @param i
* @param nameEntity
* @param jdbcType
* @throws SQLException
*/
@Override
public void setParameter(PreparedStatement ps, int i, NameEntity nameEntity, JdbcType jdbcType) throws SQLException {
// 1、驗證NameEntity
if ( null == nameEntity) {
return;
}
// 2、取出nameEntity中的值
String firstName = nameEntity.getFirstName();
String lastName = nameEntity.getLastName();
// 3、把取出的值 拼接成 一個字符串
// 自定義規則:使用 - 進行隔開
StringBuilder builder = new StringBuilder();
builder.append(firstName)
.append("-")
.append(lastName);
// 4、拼接SQL的參數
ps.setString(i,builder.toString() );
}
/**
* 這下面三個是重載,是為了解決:數據庫 ——> 對象NameEntity的流程,類似於反序列化,把另一個東西轉成正常需要的樣子
* @param resultSet
* @param columnName
* @return
* @throws SQLException
*/
@Override
public NameEntity getResult(ResultSet resultSet, String columnName ) throws SQLException {
// 1、從結果集ResultSet根據字段名取出字段值
String columnValue = resultSet.getString(columnName);
// 2、驗證columnValue
if ( null == columnValue || columnValue.length() == 0 || !columnValue.contains("-") ) {
return null;
}
// 3、根據“-”對columnValue進行拆分
String[] column = columnValue.split("-");
// 4、把拆分之后的值 給到 對象的對應值
return new NameEntity().setFirstName( column[0] ).setLastName( column[1] );
}
@Override
public NameEntity getResult(ResultSet resultSet, int i) throws SQLException {
// 1、從結果集ResultSet根據字段名取出字段值
String columnValue = resultSet.getString(i);
// 2、驗證columnValue
if ( null == columnValue || columnValue.length() == 0 || !columnValue.contains("-") ) {
return null;
}
// 3、根據“-”對columnValue進行拆分
String[] column = columnValue.split("-");
// 4、把拆分之后的值 給到 對象的對應值
return new NameEntity().setFirstName( column[0] ).setLastName( column[1] );
}
@Override
public NameEntity getResult(CallableStatement cs, int i) throws SQLException {
// 1、從CallableStatement 根據 索引取出字段值
String columnValue = cs.getString(i);
// 2、驗證columnValue
if ( null == columnValue || columnValue.length() == 0 || !columnValue.contains("-") ) {
return null;
}
// 3、根據“-”對columnValue進行拆分
String[] column = columnValue.split("-");
// 4、把拆分之后的值 給到 對象的對應值
return new NameEntity().setFirstName( column[0] ).setLastName( column[1] );
}
}
3. 注冊類型處理器
第一種(字段級別):使用@ColumnType(typeHandler = xxxx.class)
注解
注意啊:我這里是改數據庫了的,這是做的查詢嘛,要是數據庫中的數據沒符合規范,那還是查不到
第二種(全局配置):在mybatis-config.xml中進行配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="LOG4J"/>
<!--顯示開啟緩存-->
<setting name="cacheEnabled" value="true"/>
</settings>
<typeAliases>
<package name="cn.xiegongzi.entity"/>
</typeAliases>
<typeHandlers>
<!--
handler 處理器位置
javaType 要處理的是哪個對象
-->
<typeHandler handler="cn.zixieqing.handler.NameHandler"
javaType="cn.zixieqing.entity.NameEntity"/>
</typeHandlers>
</configuration>
給用到該類型的地方添加@Column
注解
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@Table(name = "user")
@ToString
public class UserEntity implements Serializable {
private static final long serialVersionUID = -5580827379143778431L;
private Integer userId;
/**
* @Transient
*
* @ColumnType(typeHandler = NameHandler.class)
*/
@Column
private NameEntity userName;
private String userSex;
private Double userSalary;
}
3、markdown筆記地址
鏈接:https://www.aliyundrive.com/s/yEMddoGw1KJ