MyBatis配置详解
# 前言
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。XML
配置文件中包含了对 MyBatis 系统的核心设置。
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">
<!--mybatis核心配置文件-->
<configuration>
<!--添加properties配置文件路径(外部配置、动态替换)-->
<properties resource="jdbc.properties" />
<!--配置环境-->
<environments default="development">
<environment id="development">
<!--配置事务管理-->
<transactionManager type="JDBC"></transactionManager>
<!--配置数据源(连接池)-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 每一个mapper.xml都需要在这个配置,获取指定的Mapper.xml文件-->
<mappers>
<mapper resource="mapper/UserDao.xml"/>
</mappers>
</configuration>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
这是第一个入门案例中配置的mybatis-config.xml配置文件,是MyBatis的核心配置文件。MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
# 1、properties(属性)
# 1.1、properties使用
properties配置的属性相当于Java中类的成员变量(属性变量)。
<!--添加properties配置文件路径(外部配置、动态替换)-->
<properties resource="jdbc.properties" />
2
resource指向文件路径地址,读取jdbc.properties文件中的内容,并把内容加载到properties中。
上面的这个配置还可以不读取jdbc.properties配置文件,直接设置属性值。
<!-- 定义变量 -->
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/xygalaxy?useSSL=false&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
<!-- 使用上面定义的变量设置为环境值 -->
<environments default="development">
<environment id="development">
<!--配置数据源(连接池)-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
resource指定配置文件和属性定义可以同时存在
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
2
3
4
# 1.2、Java配置方式使用
java.util
包下提供了Properties
类,该Properties
类继承了Hashtable
,我们知道Hashtable
是Map
的实现类,所以对于Properties
类,具有Map
的特性,也就是key-value
格式存储。
Properties的set和get
Properties properties = new Properties();
properties.setProperty("driver","com.mysql.jdbc.Driver");
properties.setProperty("url","jdbc:mysql://localhost:3306/xygalaxy?useSSL=false&useUnicode=true&characterEncoding=utf8");
properties.setProperty("username","root");
properties.setProperty("password","123456");
String driverClass = properties.getProperty("driver");
String url = properties.getProperty("url");
String username = properties.getProperty("username");
String password = properties.getProperty("password");
2
3
4
5
6
7
8
9
10
11
读取jdbc.properties配置文件
MyBatis提供了一个Resources类可以方便的读取类路径下的配置文件,使用方法如下:
InputStream inputStream = Resources.getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
roperties.load(inputStream);
String driverClass = properties.getProperty("jdbc.driver");
String url = properties.getProperty("jdbc.url");
String username = properties.getProperty("jdbc.username");
String password = properties.getProperty("jdbc.password");
2
3
4
5
6
7
8
配置MyBatisConfig的属性
// @PropertySource("jdbc.properties") 配合@Value("${key}")使用
public class MyBatisConfig {
//定义属性 为属性注入数据(数据的来源上面引入的jdbc.properties文件)
private static String driverClass;
private static String url;
private static String username;
private static String password;
/**
* @PropertySource()读取不了jdbc.properties文件,没找到什么原因,换用构造读取写入
* @throws IOException
*/
static {
Properties properties = new Properties();
try {
InputStream inputStream = Resources.getResourceAsStream("jdbc.properties");
properties.load(inputStream);
} catch (IOException e) {
log.error("读取jdbc.properties文件失败",e);
}
driverClass = properties.getProperty("jdbc.driver");
url = properties.getProperty("jdbc.url");
username = properties.getProperty("jdbc.username");
password = properties.getProperty("jdbc.password");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 2、settings(设置)
settings(设置)是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。每项都有对应的默认值,可以按需修改。
# 2.1、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">
<!--mybatis核心配置文件-->
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
</configuration>
2
3
4
5
6
7
8
9
10
11
一个配置完整的 settings 元素,参考:Mybatis-设置(settings) (opens new window)
<settings>
<!-- 开启缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 启用侵略性延迟加载 -->
<setting name="aggressiveLazyLoading" value="true"/>
<!-- 启用多个结果集 -->
<setting name="multipleResultSetsEnabled" value="true"/>
<!-- 使用列标签 -->
<setting name="useColumnLabel" value="true"/>
<!-- 不使用生成的主键 -->
<setting name="useGeneratedKeys" value="false"/>
<!-- 自动映射的行为设置为PARTIAL -->
<setting name="autoMappingBehavior" value="PARTIAL"/>
<!-- 未知列的自动映射行为设置为WARNING -->
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<!-- 默认的执行类型是SIMPLE -->
<setting name="defaultExecutorType" value="SIMPLE"/>
<!-- 默认的语句超时时间为25秒 -->
<setting name="defaultStatementTimeout" value="25"/>
<!-- 默认的fetch size为100 -->
<setting name="defaultFetchSize" value="100"/>
<!-- 不使用安全的行边界 -->
<setting name="safeRowBoundsEnabled" value="false"/>
<!-- 启用安全的结果处理器 -->
<setting name="safeResultHandlerEnabled" value="true"/>
<!-- 不将下划线映射为驼峰式命名 -->
<setting name="mapUnderscoreToCamelCase" value="false"/>
<!-- 本地缓存的范围设置为SESSION -->
<setting name="localCacheScope" value="SESSION"/>
<!-- 对于null的jdbc类型设置为OTHER -->
<setting name="jdbcTypeForNull" value="OTHER"/>
<!-- 当触发懒加载的方法为equals, clone, hashCode, toString -->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
<!-- 默认的脚本语言驱动是org.apache.ibatis.scripting.xmltags.XMLLanguageDriver -->
<setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/>
<!-- 默认的枚举类型处理器是org.apache.ibatis.type.EnumTypeHandler -->
<setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumTypeHandler"/>
<!-- 不在null上调用setter方法 -->
<setting name="callSettersOnNulls" value="false"/>
<!-- 对于空行不返回实例 -->
<setting name="returnInstanceForEmptyRow" value="false"/>
<!-- 日志前缀为exampleLogPreFix_ -->
<setting name="logPrefix" value="exampleLogPreFix_"/>
<!-- 日志实现为SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING -->
<setting name="logImpl" value="SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING"/>
<!-- 代理工厂为CGLIB或JAVASSIST -->
<setting name="proxyFactory" value="CGLIB | JAVASSIST"/>
<!-- VFS实现为org.mybatis.example.YourselfVfsImpl -->
<setting name="vfsImpl" value="org.mybatis.example.YourselfVfsImpl"/>
<!-- 使用实际的参数名 -->
<setting name="useActualParamName" value="true"/>
<!-- 配置工厂为org.mybatis.example.ConfigurationFactory -->
<setting name="configurationFactory" value="org.mybatis.example.ConfigurationFactory"/>
</settings>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 2.2、Java配置方式使用
MyBatis更趋向于XML配置方式,对于Java配置方式配置最后章节会讲,这里只是说明对于MyBatis配置来说,XML方式和Java配置方式都可以。SpringBoot开发项目时这些都会简化配置,现在这些只是学习过程。
resources/config下创建mybatis核心配置文件mybatis-config-spring.xml
,数据源和SqlSessionFactoryBean我们已经都交给Spring管理了,这里就不需要额外的配置了,只要设置一些我们需要的就行。
<?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">
<!--mybatis核心配置文件-->
<configuration>
<settings>
<!-- 开启缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
</configuration>
2
3
4
5
6
7
8
9
10
11
12
13
MyBatisConfig配置类中,将核心配置加入
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(resolveMapperLocations());
// 设置核心配置文件
sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("classpath*:config/mybatis-config-spring.xml"));
return sqlSessionFactoryBean;
}
2
3
4
5
6
7
8
9
对于开启缓存这里说两点配置方式,后面会详细说明。
- 注解开启缓存
@CacheNamespace(implementation = org.mybatis.caches.ehcache.EhcacheCache.class)
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
UserPO getUserById(int id);
}
2
3
4
5
@CacheNamespace 是 MyBatis-Spring 集成中的一个注解,用于启用缓存。并指定了 Ehcache 作为缓存实现(可以不指定用默认的)。
- Mapper.xml中开启缓存
<!-- useCache="true"表示当前查询开启二级缓存,一级缓存默认开启 -->
<!-- flushCache的默认值是false,表示任何时候语句被调用,都不会去清空本地缓存和二级缓存。 -->
<select id="getUserList" resultType="com.xygalaxy.pojo.UserPO" useCache="true" flushCache="true">
select * from user
</select>
<!-- 表示整个Mapper映射文件 -->
<mapper namespace="com.xygalaxy.mapper.UserMapper">
<!--当前映射文件开启二级缓存-->
<cache></cache>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
# 3、typeAliases(类型别名)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
MyBatis提供了三种方式来设置类型别名:
- 单一类型设置别名
- 包全局设置别名
- 注解设置别名
# 3.1、单一类型设置别名
typeAliases需要配置在environments之前
<?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">
<!--mybatis核心配置文件-->
<configuration>
<!-- 配置别名 -->
<typeAliases>
<typeAlias alias="user" type="com.xygalaxy.pojo.UserPO"/>
</typeAliases>
</configuration>
2
3
4
5
6
7
8
9
10
11
UserMapper.xml中使用时
<!-- 原来的resultType为全限定类名 -->
<select id="getUserList" resultType="com.xygalaxy.pojo.UserPO">
select * from user
</select>
<!-- 直接使用别名 -->
<select id="getUserList" resultType="user">
select * from user
</select>
2
3
4
5
6
7
8
9
# 3.2、包全局设置别名
指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,会使用 Bean 的首字母小写的非限定类名来作为它的别名。比如com.xygalaxy.pojo.UserPO
的别名为userPO
。
<?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">
<!--mybatis核心配置文件-->
<configuration>
<!-- 配置别名 -->
<typeAliases>
<!-- 扫描整个包 -->
<package name="com.xygalaxy.pojo"/>
</typeAliases>
</configuration>
2
3
4
5
6
7
8
9
10
11
12
# 3.3、注解设置别名
在没有注解的情况下,包扫描后,UserPO
的别名为userPO
。但加了注解后,别名就为注解中的别名user
。
@Alias("user")
public class UserPO {
}
2
3
4
# 3.4、Java配置方式配置别名
通过SqlSessionFactoryBean设置别名包扫描,如果是单个的话,就用注解简单些。
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 别名包扫描
sqlSessionFactoryBean.setTypeAliasesPackage("com.xygalaxy.pojo");
return sqlSessionFactoryBean;
}
2
3
4
5
6
7
# 4、typeHandlers(类型处理器)
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。
MyBatis中支持的类型处理器很完善了,大多数情况都可以直接使用,不需要自己定义。
# 4.1、自定义类型处理器
自定义类型处理器方式有两种,实现TypeHandler
接口,或继承抽象类BaseTypeHandle
,并可以指定转换后的字段类型
BaseTypeHandler也是继承了TypeHandler接口,在实现的TypeHandler接口的方法中调用的是自身抽象方法。
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
public T getResult(ResultSet rs, String columnName) throws SQLException {
Object result;
try {
result = this.getNullableResult(rs, columnName);
} catch (Exception var5) {
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + var5, var5);
}
return rs.wasNull() ? null : result;
}
public abstract T getNullableResult(ResultSet var1, String var2) throws SQLException;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 指定处理类
<!-- mybatis-config.xml中配置全局的类型处理器 -->
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
<!-- 局部类型处理,在Mapper中指定需要转换的字段,jdbcType指数据库中的类型,javaType指Java中的类型,typeHandler指定自定义的转换器 -->
<result column="flag" property="flag" jdbcType="TINYINT" javaType="java.lang.String" typeHandler="com.rangers.MyTypeHandler"></result>
2
3
4
5
6
7
- 继承BaseTypeHandler类,重写处理方法
@MappedJdbcTypes(value="JdbcType.VARCHAR",includeNullJdbcType=false)
public class ExampleTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@MappedJdbcTypes 注解有两个属性可以设置,全局处理器可以设置:
- value:用于指定支持的 JdbcType 类型。这里设置为 JdbcType.VARCHAR 表示我们的自定义 Java 类型可以映射到数据库的 VARCHAR 类型。
- includeNullJdbcType:用于指定是否要将 null 值的数据库类型也包括在内。默认情况下,该属性为 false,即不包括 null 值的类型。
想了解更多参考:类型处理器(typehandlers) (opens new window)
# 4.2、处理枚举类型
在开发中,我们用到的类型处理可能相对较少,但是枚举类的处理会非常多,Mybatis可以将枚举中的值进行转换使用,比如:数据库中的User表,性别字段,数据库保存了man、women表示,但是我们需要返回结果为男、女,一般这类固定的都用枚举类保存,这中间就需要进行转换。
MyBatis内置两个枚举转换器:org.apache.ibatis.type.EnumTypeHandler
、org.apache.ibatis.type.EnumOrdinalTypeHandler
。
- EnumTypeHandler:这是默认的枚举转换器,该转换器将枚举实例转换为实例名称的字符串,即将UserSexEnum.MAN转换MAN。
- EnumOrdinalTypeHandler:这个转换器将枚举实例的ordinal属性作为取值,从0开始,比如:数据表中为1,则枚举取值为UserSexEnum.WOMAN。
- 默认拦截器配置
<?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">
<!--mybatis核心配置文件-->
<configuration>
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler" javaType="com.xygalaxy.enums.UserSexEnum"/>
</typeHandlers>
</configuration>
2
3
4
5
6
7
8
9
10
新增枚举UserSexEnum
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum UserSexEnum{
MAN,WOMAN;
private String sexValue;
}
2
3
4
5
6
7
8
9
UserPO新增枚举字段
@Data // 提供get和set方法
@AllArgsConstructor // 全构造
@NoArgsConstructor // 无参构造
public class UserPO implements Serializable {
private Integer id;
private String username;
private String password;
private String email;
private UserSexEnum sex;
}
// 测试
@Test
public void testEnum(){
UserPO userPO = userMapper.selectAllUserList().get(0);
log.info("Enum ==> "+userPO.getSex()+" ===>"+userPO);
}
// 结果:Enum ==> MAN ===>UserPO(id=1, username=updateUser, password=323232, email=222@qq.com, sex=MAN)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
使用EnumTypeHandler结果
数据库是MAN,对应的sex枚举也是MAN
使用EnumOrdinalTypeHandler结果
数据库是0,对应的sex枚举是第一个值,也就是MAN,如果数据库是1,那么就是WOMAN。
# 4.3、自定义枚举转换器
MyBatis提供的内置枚举类型处理都是对应简单的枚举转换,也不怎么好用,我们可以自定义枚举使用,以下是通用的枚举转换器,并兼容默认的枚举转换器。
- 定义枚举接口BaseEnum
public interface BaseEnum<K,V> {
K getKey();
V getValue();
}
2
3
4
5
6
7
- 新增UserSexEnum枚举类并实现枚举接口,将key和value返回,如果只有一个值的,可以都一样,或者用默认的枚举转换器就行
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum UserSexEnum implements BaseEnum {
MAN("man","男"),
WOMAN("woman","女");
private String sexKey;
private String sexValue;
@Override
public String getKey() {
return sexKey;
}
@Override
public String getValue() {
return sexValue;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- 新增枚举处理器CodeEnumTypeHandler
public class CodeEnumTypeHandler<E extends Enum<?> & BaseEnum> extends BaseTypeHandler<BaseEnum> {
private Class<E> type;
public CodeEnumTypeHandler(Class<E> type) {
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.type = type;
}
/**
* 写入数据库时调用,用于定义设置参数时,该如何把Java类型的参数转换为对应的数据库类型
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, BaseEnum parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter.getKey().toString());
}
/**
* getNullableResult三个方法是在查询结果集中获取指定列的枚举值,一个是根据列名获取值,一个是根据列索引位置获取值,最后一个是存储过程。
*/
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
String code = rs.getString(columnName);
return rs.wasNull() ? null : getCodeValue(code);
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String code = String.valueOf(rs.getInt(columnIndex));
return rs.wasNull() ? null : getCodeValue(code);
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String code = String.valueOf(cs.getInt(columnIndex));
return cs.wasNull() ? null : getCodeValue(code);
}
private E getCodeValue(String code){
E[] enumConstants = type.getEnumConstants();
return Arrays.stream(enumConstants)
.filter(i -> i.getKey().equals(code))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("未知的枚举类型:" + code + ",请核对" + this.type.getSimpleName()));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
- 兼容原有枚举处理器,如果有需要兼容,则用AutoEnumTypeHandler,如果觉得没必要,直接用CodeEnumTypeHandler
public class AutoEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
private BaseTypeHandler typeHandler = null;
public AutoEnumTypeHandler(Class<E> type) {
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
// 如果是枚举,并且实现了BaseAutoEnum,则用自定义枚举转换
if(type.isEnum()&&BaseEnum.class.isAssignableFrom(type)){
typeHandler = new CodeEnumTypeHandler(type);
}else {
// 默认转换器 也可换成 EnumOrdinalTypeHandler
typeHandler = new EnumTypeHandler<>(type);
}
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
typeHandler.setNonNullParameter(ps,i, parameter,jdbcType);
}
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
return (E) typeHandler.getNullableResult(rs,columnName);
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return (E) typeHandler.getNullableResult(rs,columnIndex);
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return (E) typeHandler.getNullableResult(cs,columnIndex);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
- 配置枚举处理器为自定义处理器
<?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">
<!--mybatis核心配置文件-->
<configuration>
<typeHandlers>
<!-- handler自定义处理器,javaType需要处理的枚举类型 -->
<typeHandler handler="com.xygalaxy.handler.AutoEnumTypeHandler" javaType="com.xygalaxy.enums.UserSexEnum"/>
</typeHandlers>
</configuration>
2
3
4
5
6
7
8
9
10
11
- 测试
@Test
public void testEnum(){
UserPO userPO = userMapper.selectAllUserList().get(0);
log.info("Enum ==> "+userPO.getSex().getValue()+" ===>"+userPO);
}
// 结果:Enum ==> 男 ===>UserPO(id=1, username=updateUser, password=323232, email=222@qq.com, sex=MAN)
2
3
4
5
6
7
# 5、objectFactory(对象工厂)
每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。比如:
// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {
@Override
public <T> T create(Class<T> type) {
return super.create(type);
}
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return super.create(type, constructorArgTypes, constructorArgs);
}
@Override
public void setProperties(Properties properties) {
super.setProperties(properties);
}
@Override
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>
2
3
4
ObjectFactory 接口很简单,它包含两个创建实例用的方法,一个是处理默认无参构造方法的,另外一个是处理带参数的构造方法的。 另外,setProperties 方法可以被用来配置 ObjectFactory,在初始化你的 ObjectFactory 实例后, objectFactory 元素体中定义的属性会被传递给 setProperties 方法。
# 6、plugins(插件)
MyBatis的插件实际上是一个拦截器,可以在MyBatis的各个操作(如查询、插入、更新、删除等)执行前后进行拦截,并对这些操作进行增强或修改。MyBatis的插件可以作用于MyBatis中的四大接口,分别为:Executor,ParameterHandler,ResultSetHandler和StatementHandler。
可作用接口 | 可作用方法 | 拦截器用途 |
---|---|---|
Executor | update(),query(),flushStatements(),commit(),rollback(),getTransaction(),close(),isClosed() | 拦截执行器中的方法 |
ParameterHandler | getParameterObject(),setParameters() | 拦截对参数的处理 |
ResultSetHandler | handleResultSets(),handleOutputParameters() | 拦截对结果集的处理 |
StatementHandler | prepare(),parameterize(),batch(),update(),query() | 拦截SQL构建的处理 |
# 6.1、自定义插件使用
- 定义拦截器,实现Interceptor接口
@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class ExecutorInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取被拦截的对象
Object target = invocation.getTarget();
log.info("target=============>"+target);
// 获取被拦截的方法
Method method = invocation.getMethod();
log.info("method=========>"+method);
// 获取被拦截的方法的参数
Object[] args = invocation.getArgs();
log.info("args=========>"+args);
log.info("start Before=========>");
// 执行被拦截的方法
Object result = invocation.proceed();
log.info("result==========>"+result);
// 返回执行结果
return result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- @Intercepts: 这是 MyBatis 插件的核心注解,它告诉 MyBatis 这个类是一个插件,并且该插件会拦截某些方法。
- @Signature: 这个注解定义了插件要拦截的方法的详细信息。
- type: 指定要拦截的类的类型,这里是 Executor 类。这意味着这个插件会拦截 Executor 类的所有方法。
- method: 指定要拦截的具体方法名,这里是 "query"。这意味着这个插件只会拦截 Executor 类中的 query 方法。
- args: 指定 method 方法参数的类型列表。这意味着只有当 query 方法的参数类型与这里指定的类型相匹配时,该方法才会被拦截。在这个例子中,query 方法接受四个参数:MappedStatement、Object、RowBounds 和 ResultHandler。
- 配置拦截器
<!-- mybatis-config-spring.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">
<!--mybatis核心配置文件-->
<configuration>
<!-- 配置插件拦截器 -->
<plugins>
<plugin interceptor="com.xygalaxy.interceptor.ExecutorInterceptor"/>
</plugins>
</configuration>
2
3
4
5
6
7
8
9
10
11
12
- 测试
@Test
public void testExecutorInterceptor(){
UserPO userPO = userMapper.selectAllUserList().get(0);
log.info("Enum ==> "+userPO.getSex().getValue()+" ===>"+userPO);
}
2
3
4
5
插件测试结果
# 6.2、多插件执行顺序
<plugins>
<plugin intercepter="插件1"></plugin>
<plugin intercepter="插件2"></plugin>
</plugins>
2
3
4
四大对象植入插件逻辑时,是根据声明插件时的顺序从里向外一层一层的生成代理对象,反过来四大对象实际运行时,是从外向里一层一层的调用插件的逻辑。
# 7、environments(环境配置)
MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置。想要了解更多可以去看看MyBatis官网。这里比较少用,而且SpringBoot的配置不同环境更为简便。
说一下数据源(data Source)配置
<!-- default="development" 这个就是MyBatis的环境配置属性,通过id确认使用 -->
<environments default="development">
<environment id="development">
<!--配置事务管理-->
<transactionManager type="JDBC"></transactionManager>
<!--配置数据源(连接池)-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
2
3
4
5
6
7
8
9
10
11
12
13
14
type="POOLED"
- MyBatis有有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")
- UNPOOLED:这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。
- POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
- JNDI:这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。
# 8、mappers(映射器)
<!-- 每一个mapper.xml都需要在这个配置,获取指定的Mapper.xml文件-->
<mappers>
<mapper resource="mapper/UserDao.xml"/>
</mappers>
2
3
4
MyBatis查找定义的 SQL 映射语句,我们需要告诉 MyBatis 到哪里去找到这些语句。在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。
映射器查询,MyBatis提供了四种方式:
# 8.1、resource查找
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
2
3
4
5
6
# 8.2、url查找
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
2
3
4
5
6
# 8.3、class查找
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
2
3
4
5
6
# 8.4、包名查找
<!-- 将包内的映射器接口全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
2
3
4
# 9、Java配置方式
对于MyBatis的核心配置,我们通常会使用XML配置文件,但是也可以使用Java配置方式来进行配置。
@Configuration // 声明配置类
@MapperScan("com.xygalaxy.mapper") // MyBatis扫描dao层接口,也就是mapper接口
@Slf4j // 日志注解
@EnableTransactionManagement // 开启事务支持
public class MyBatisConfig {
//定义属性 为属性注入数据(数据的来源上面引入的jdbc.properties文件)
private static String driverClass;
private static String url;
private static String username;
private static String password;
/**
* @PropertySource()读取不了jdbc.properties文件,没找到什么原因,换用构造读取写入
* @throws IOException
*/
static {
Properties properties = new Properties();
try {
InputStream inputStream = Resources.getResourceAsStream("jdbc.properties");
properties.load(inputStream);
} catch (IOException e) {
log.error("读取jdbc.properties文件失败",e);
}
driverClass = properties.getProperty("jdbc.driver");
url = properties.getProperty("jdbc.url");
username = properties.getProperty("jdbc.username");
password = properties.getProperty("jdbc.password");
}
/**
* 创建数据源返回数据源,Spring会自动调用该方法,并将该对象交给IOC容器管理
* @return
*/
@Bean
public DataSource dataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driverClass);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
druidDataSource.setMaxActive(100); //连接池最大连接数
druidDataSource.setMinIdle(20); //连接池最小连接数
druidDataSource.setMaxWait(1000); //连接池超时时间
return druidDataSource;
}
/**
* 事务给Spring管理
* @param dataSource
* @return
*/
@Bean
public TransactionManager getTrans(DataSource dataSource){
DataSourceTransactionManager trans = new DataSourceTransactionManager();
//设置数据源
trans.setDataSource(dataSource);
return trans;
}
/**
* Java配置方式配置MyBatis核心配置
* @param dataSource
* @return
*/
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean2(DataSource dataSource,TransactionManager transactionManager) throws Exception {
// 创建SqlSessionFactoryBean
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 数据源设置
sqlSessionFactoryBean.setDataSource(dataSource);
// 事务管理
TransactionFactory transactionFactory = (TransactionFactory) transactionManager;
sqlSessionFactoryBean.setTransactionFactory(transactionFactory);
// 创建MyBatis核心配置中的环境
Environment environment = new Environment("development",transactionFactory,dataSource);
// 创建MyBatis核心配置整个Configuration
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(environment);
// 将MyBatis整个核心配置给SqlSessionFactoryBean
sqlSessionFactoryBean.setConfiguration(configuration);
// 配置类型别名扫描包
sqlSessionFactoryBean.setTypeAliasesPackage("com.xygalaxy.pojo");
// mappers(映射器)映射文件设置
sqlSessionFactoryBean.setMapperLocations(resolveMapperLocations());
// MyBatis核心配置xml方式读取,这里我们完全不用这种,如果用这种,可以不用后面的那些配置了,看个人喜欢哪种方式
//sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("config/mybatis-config-spring.xml"));
// 类型转换注册器设置
sqlSessionFactoryBean.setDefaultEnumTypeHandler(AutoEnumTypeHandler.class);
return sqlSessionFactoryBean;
}
/**
* 扫描mapper的文件,获取resources/mapper下,所有的mapper.xml文件
* @return
*/
public Resource[] resolveMapperLocations() {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
Resource[] mappers= null;
try {
mappers = resourceResolver.getResources("classpath*:mapper/*.xml");
} catch (IOException e) {
log.error("加总mapper文件失败");
}
return mappers;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116