mybatis配置详解

160 阅读5分钟

本文是Mybatis配置的详细说明。

Mybatis是为数不多的跟着官网学习,就能学明白的,建议多看看官方文档。

传送门:Mybatis官网

    上一篇mybatis配置和基本使用CRUD    ←||→    下一篇Mybatis动态sql

官网提供:

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

属性(properties)

1、可以通过properties标签的resource或者url属性来引入外部配置文件。

2、也可以使用他的子标签property来配置具体属性。

3、也可以在代码中显示的引入Properties对象

以上有三个配置属性的地方,聊一聊优先级。

先说结论:
加载顺序: 2 -> 1-> 3
优先级: 3 ->1 -> 2
mybatis会层层覆盖之前的属性。
源码说明:源码路径:org.apache.ibatis.builder.xml.XMLConfigBuilder image-20220309102026465.png

这个XNode context节点对象,就代表<properties></>节点。

  • defaults就是将<Properties></>标签内的子节点封装成,Properties对象。 也就是说先读取mybatis配置文件内的配置。
  • resourceurl分别是引入配置的两种方式(一般用resource),两种方式不能同时使用。
    resource方式说明:当resource!=null时,也就是配置了外部配置文件,就会defaults.putAll,如果有重复的配置,就会覆盖。
  • 最后Properties vars = configuration.getVariables();就是将代码形式传入的props对象读取出来,并覆盖之前的配置。(Variables会在实例化XmlConfigration时赋值[就是一个props])
测试说明配置生效顺序
  • db.properties里的username注释掉

    image-20220309104341596.png

  • mybatis配置文件

    <properties  resource="db.properties">
        <property name="username" value="root"/>
    </properties>
    
  • 测试

    @Test
    public void testConn2() throws IOException {
        SqlSession sqlSession = mybatisUtil.OpenSqlSession();
        System.out.println(sqlSession);
        System.out.println(sqlSession.getConnection());
    }
    
  • 结果:可以获取连接。

image-20220309110033958.png


> **`db.properties`覆盖`mybatis-config.xml`配置:**

稍作修改:给一个错误的用户名

 username=rootxxx

测试:和上面一样,只做获取连接操作。
结果: image-20220309110623990.png 报错信息:本地用户rootxxx用户名和密码不匹配,也就是db.properties的配置覆盖了mybatis.xml的配置且奏效了。
如果修改为正确的用户名是没有问题的username=root


使用代码传参的方式覆盖配置
我们SqlSessionFactoryBuilderbuild()方法构建sqlsessionFactory对象,build()方法是有重载的,其中一个重载就是:

image-20220309111258408.png

也就是说可以这样操作:

 mybatisUtil:
 Properties properties = new Properties();
 properties.setProperty("username","rootxx");
 sqlSessionFactory = new SqlSessionFactoryBuilder().build(in,properties);

db.propertiesmybatis-config.xml配置都搞好,不报错。
测试: image-20220309111625485.png

说明配置还是被我们传入的props属性覆盖了。


设置(setting)

[mybatis官网]:一个完整的setting

 <settings>
   <-- mybatis一级缓存      默认开启动 !-->
   <setting name="cacheEnabled" value="true"/>
   <setting name="lazyLoadingEnabled" value="true"/>
   <setting name="multipleResultSetsEnabled" value="true"/>
   <setting name="useColumnLabel" value="true"/>
   <setting name="useGeneratedKeys" value="false"/>
   <setting name="autoMappingBehavior" value="PARTIAL"/>
   <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
   <setting name="defaultExecutorType" value="SIMPLE"/>
   <setting name="defaultStatementTimeout" value="25"/>
   <setting name="defaultFetchSize" value="100"/>
   <setting name="safeRowBoundsEnabled" value="false"/>
   <setting name="mapUnderscoreToCamelCase" value="false"/>
   <setting name="localCacheScope" value="SESSION"/>
   <setting name="jdbcTypeForNull" value="OTHER"/>
   <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
   <setting name="logImpl" value="STDOUT_LOGGING"/>
 </settings>

一般来说配驼峰命名和日志就行,其他使用默认。

别名(TypeAlias)

两种配置方式

1、配置包

 <typeAliases>
     <package name="com.roily.entity"/>
 </typeAliases>

2、配置具体类

 <typeAliases>
     <typeAlias type="com.roily.entity.Department" alias="dept"/>
 </typeAliases>

说明:两种配置方式不能同时存在

最终别名和类的映射关系还是会放入typeAliasRegistry中。 image-20220309141038837.png

注解配置别名:

1、扫描包加注解
配置:

 <typeAliases>
     <package name="com.roily.entity"/>
 </typeAliases>
 ​
 <select id="queryBatchAliasTest" resultType="ddd">
     select * from department
 </select>

实体类加注解:@Alias("ddd")

接口:

//查询所有记录
List<Department> queryBatchAliasTest();

也是可以的

2、第二种方式加注解

配置:

 <typeAlias type="com.roily.entity.Department" alias="deptAlias"/>
 ​
 <select id="queryBatchAliasTest2" resultType="ddd">
     select * from department
 </select>

实体类加注解:@Alias("ddd")

接口:

 //查询所有记录
 List<Department> queryBatchAliasTest();

报错: image-20220309144224769.png

修改配置mapper.xml

<select id="queryBatchAliasTest2" resultType="deptAlias">
    select * from department
</select>

可以的

总结:建议直接使用扫描包以类名首字母小写为类别名。 是在想使用注解的,还是基于扫描包配置。给特定的实体添加特定别名,此配置会覆盖类名首字母小写别名的配置。

typeHandlers(类型处理器)

这个是一个很重要的配置,之前我们遇到过,javautil.date类型可以直接转化为timeStamp类型存到数据库中,就是因为DateTypeHandler这个处理器。

但是一般我们不去配这个,mybatis提供的已经够用了。

mybatis提供的默认的类型处理器

跟着官网自己写一个类型处理器:

第一步:创建一个类实现BaseTypeHandler接口

myCustomTypeHandler自定义的类型处理器,泛型接口,让这个处理器只处理VARCHAR类型数据

 @MappedJdbcTypes(JdbcType.VARCHAR)
 public class myCustomTypeHandler extends BaseTypeHandler<String> {
 ​
     public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
         System.out.println("==========================");
         ps.setString(i,parameter+"XXX");
     }
     public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
         String string = rs.getString(columnName);
         return string+"拿取数据1";
     }
     public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
         String string = rs.getString(columnIndex);
         return string+"拿取数据2";
     }
     public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
         String string = cs.getString(columnIndex);
         return string+"拿取数据2";
     }
 }

配置mybatis.xml

 <typeHandlers>
     <typeHandler handler="com.roily.typeHandler.myCustomTypeHandler"/>
 </typeHandlers>

测试:

1、取数据

  @Test
 public void test01(){
     SqlSession sqlSession = mybatisUtil.OpenSqlSession();
     DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
     List<Department> departments = mapper.queryBatch();
     for (Department department : departments) {
         System.out.println(department);
     }
 }

结果: image-20220309151821461.png

结论:查询语句还是那样没变,会对查询结果进行处理,也就是从ResultSet拿数据放入实体时进行处理,这里只有department字段是varchar类型,所以根据自定义的类型处理器做了相应的处理。

2、插入数据

同样的没有开启sqlsession自动提交的话,需要手动提交事务

 @Test
 public void test02() {
     SqlSession sqlSession = mybatisUtil.OpenSqlSession();
     DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
     Department dept = Department.builder()
             .deptName("handler")
             .delete(0)
             .modifyTime(new Date())
             .createTime(new Date())
             .build();
     int i = mapper.insertIntoDept(dept);
     sqlSession.commit();
     System.out.println(i);
 }

结果:

没有触发处理器 image-20220309154635587.png 原因:没有设置jdbcType

 <insert id="insertIntoDept">
     insert into department(`deptName`,`delete`,`create_time`,`modify_time`)
     values (#{deptName,jdbcType=VARCHAR},#{delete},#{createTime},#{modifyTime})
 </insert>

image-20220309154557688.png

image-20220309154954483.png

环境配置(environments)

environment(环境变量)

  • transactionManager(事务管理器)
  • dataSource(数据源)
 <environments default="dev1">
     <environment id="dev1">
         <transactionManager type="JDBC"/>
         <dataSource type="POOLED">
             <property name="driver" value="${driver}"/>
            .....
         </dataSource>
     </environment>
     <environment id="dev2">
         <transactionManager type="JDBC"/>
         <dataSource type="POOLED">
             <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
             .......
         </dataSource>
     </environment>
 </environments>

环境可以配置多个,但需要指定默认的环境default

事务处理器为JDBC。用jdbc的,契合容器框架。代码出自:org.apache.ibatis.session.Configration

 typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
 typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

数据源

 typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
 typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
 typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

pooled& unpooled就是池化技术与非池化技术,池化技术的数据源基于非池化实现。

JNDI个人不理解,欢迎讨论。

换源

使用Druid作为mybatis的数据源

依赖:

 <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>druid</artifactId>
     <version>1.2.8</version>
 </dependency>

自己写一个DruidDataSourceFactory

 public class DruidDataSourceFactory extends PooledDataSourceFactory {
     public DruidDataSourceFactory(){
         this.dataSource = new DruidDataSource();
     }
     @Override
     public DataSource getDataSource() {
         try {
             ((DruidDataSource)this.dataSource).init(); //初始化Druid数据源
         } catch (SQLException throwables) {
             throw new RuntimeException(throwables);
         }
         return this.dataSource;
     }
 }

配置(mybatis-config.xml):

 <environment id="druid">
     <transactionManager type="JDBC"/>
     <dataSource type="com.roily.util.DruidDataSourceFactory">
         <property name="driverClassName" value="${driver}"/>
         <property name="url" value="${url}"/>
         <property name="username" value="${username}"/>
         <property name="password" value="${password}"/>
     </dataSource>
 </environment>

测试:

 @Test
 public void test01() {
     SqlSession sqlSession = mybatisUtil.OpenSqlSession();
     DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
     List<Department> departments = mapper.queryBatch();
     for (Department department : departments) {
         System.out.println(department);
     }
 }

结果:使用Druid作为数据源,且成功获取连接。

image-20220309223046340.png

为什么需要自己定义?

Druid提供了两个数据源工厂,但都不是mybatis想要的,那么又是为什么?mybatis源码解析

image-20220309182441626.png mybatis想要的:通过反射创建对象。

DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();

image-20220309223310967.png 这里还有一个有意思的事情,druid数据源短语Driver驱动的配置,对于名字也是有要求的。

image-20220309223522200.png 如果有兴趣可以debug一下,propertyName的值为driverClassName结果是。后期有时间我也可以分析一下
这是因为DruidDateSource的父类DruidAbstractDataSourceset方法:

image-20220309223826263.png

mapper配置
  • resource方式 , 也是最常用的方式
 <mappers>
     <mapper resource="xml/DepartmentMapper.xml"/>
 </mappers>
  • URl方式 绝对路径(唯一资源定位)
 <mapper url="file:E:/programmeTools/idea/git/JavaBase/Mybatis-02/src/main/resources/xml/DepartmentMapper.xml"/>
  • class方式。
 <mapper class="com.roily.mapper.DepartmentMapper"/>
  1. mapper.xmlmapper结构名字一样
  2. 在同一目录下
  3. 配置maven静态资源过滤 image-20220309225314093.png

总结:

配置按规矩配,避免不必要麻烦。

mybatis配置总结:

db.properties数据库配置文件

 driver=com.mysql.cj.jdbc.Driver
 url=jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&charactEncoding=utf8&useSSL=true
 username=root
 password=123456

mybatis-config.xml mybatis核心配置文件:

 <?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>
     <!--  映入配偶之文件  -->
     <properties resource="db.properties">
     </properties>
     <settings>
         <setting name="mapUnderscoreToCamelCase" value="true"/>
         <setting name="logImpl" value="STDOUT_LOGGING"/>
     </settings>
 ​
     <typeAliases>
         <package name="com.roily.entity"/>
     </typeAliases>
     <typeHandlers>
         <!--处理器,没啥用-->
         <typeHandler handler="com.roily.typeHandler.myCustomTypeHandler" javaType="String" jdbcType="VARCHAR"/>
     </typeHandlers>
     
     <environments default="druid">
         <environment id="druid">
             <transactionManager type="JDBC"/>
             <dataSource type="com.roily.util.DruidDataSourceFactory">
                 <property name="driverClassName" value="${driver}"/>
                 <property name="url" value="${url}"/>
                 <property name="username" value="${username}"/>
                 <property name="password" value="${password}"/>
             </dataSource>
         </environment>
         <environment id="test1">
             <transactionManager type="JDBC"/>
             <dataSource type="POOLED">
                 <property name="driver" value="${driver}"/>
                 <property name="url" value="${url}"/>
                 <property name="username" value="${username}"/>
                 <property name="password" value="${passwd}"/>
             </dataSource>
         </environment>
     </environments>
     <mappers>
         <mapper resource="xml/DepartmentMapper.xml"/>
         <mapper resource="xml/DepartmentMapper.xml"/>
     </mappers>
 </configuration>

后期添加:mybatis源码分析