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

Mybatis的配置文件包含了会深深影响Mybatis行为的设置和属性信息

配置(configuration)

在mybatis-config.xml文件中标签都有规定的顺序,需要按照以下顺序添加

properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?

属性(properties)

我们可以通过properties来实现引用文件。Mybatis的一些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在properties元素的子元素中设置

  1. 编写一个配置文件(db.properties)
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/learn?serverTimezone=GMT&useSSL=false&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456789
  1. 在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> <!--引入外部配置文件-->
<properties resource="db.properties" /> <!--也可以配置文件中写一部分,标签中写一部分-->
<!--优先使用外部配置文件中的配置-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="pwd" value="123456789"/>
</properties> <environments default="test"> <environment id="test">
<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="${password}"/>
</dataSource>
</environment>
</environments> <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
  1. 测试
@Test
public void test() {
//获取SQLSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession(); try { UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList(); for (User user : userList) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭SQLSession
sqlSession.close();
}
}

Mybatis会先读取properties元素体内的指定属性,再根据resource属性读取类路径下属性文件。所以外部配置文件中编写的配置会覆盖内部标签编写的配置,外部配置文件拥有更高优先级

设置(setting)

这是Mybatis中极为重要的调整设置,他们会改变Mybatis的运行时行为

更多设置请参考:https://mybatis.org/mybatis-3/zh/configuration.html#settings

环境配置(environments)

Mybatis可以配置成适应多种环境

尽管可以配置多个环境,但每个SQLSessionFactory实例只能选择一种环境

Mybatis默认的事务管理器就是JDBC,数据源是POOLED

<environments default="test">	<!--需要使用的环境-->

    <!--环境一-->
<environment id="development"> <!--id:每套environment的唯一标识-->
<transactionManager type="JDBC"/> <!--事务管理器的配置-->
<dataSource type="POOLED"> <!--数据源的配置-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/learn?serverTimezone=GMT&amp;useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456789"/>
</dataSource>
</environment> <!--环境二-->
<environment id="test">
<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="${password}"/>
</dataSource>
</environment>
</environments>

类型别名(typeAliases)

类型别名为Java类型设置一个短的名字,它仅用于XML配置,存在的意义仅在于用来减少类完全限定名的冗余

<typeAliases>
<typeAlias alias="User" type="com.luoqing.model.User"/>
</typeAliases>

也可以指定一个包名,Mybatis会在包名下搜索需要的JavaBean

<typeAliases>
<package name="com.luoqing.model" />
</typeAliases>

每一个在包名下的JavaBean,在没有注解的情况下,会使用Bean首字母小写的非限定类名来作为它的别名

如果有注解则其别名为其注解值

//这个类的别名为user
@Alias("user")
public class User {
……
}

在实体类比较少的时候,使用第一种方式。如果实体类十分多,建议使用第二种

类型处理器(typeHandlers)

Mybatis使用typeHandlers将数据库中字段的类型转换为java类中属性的类型,或将java类中属性的类型转换为数据库中字段的类型

通过Configuration对象获取

@Test
public void myTest() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
TypeHandlerRegistry typeHandlerRegistry = sqlSession.getConfiguration().getTypeHandlerRegistry();
Collection<TypeHandler<?>> list = typeHandlerRegistry.getTypeHandlers();
System.out.println(list.size());
for (TypeHandler<?> typeHandler : list) {
System.out.println(typeHandler.getClass().getName());
} sqlSession.close(); }

执行结果如下。以下所有的处理器类位于mybatis-X.X.X.jar/org/apache/ibatis/type中

40
org.apache.ibatis.type.InstantTypeHandler
org.apache.ibatis.type.MonthTypeHandler
org.apache.ibatis.type.JapaneseDateTypeHandler
org.apache.ibatis.type.UnknownTypeHandler
org.apache.ibatis.type.DateTypeHandler
org.apache.ibatis.type.CharacterTypeHandler
org.apache.ibatis.type.BigIntegerTypeHandler
org.apache.ibatis.type.SqlxmlTypeHandler
org.apache.ibatis.type.LocalDateTimeTypeHandler
org.apache.ibatis.type.ArrayTypeHandler
org.apache.ibatis.type.YearTypeHandler
org.apache.ibatis.type.FloatTypeHandler
org.apache.ibatis.type.NStringTypeHandler
org.apache.ibatis.type.BooleanTypeHandler
org.apache.ibatis.type.ByteTypeHandler
org.apache.ibatis.type.ClobTypeHandler
org.apache.ibatis.type.BigDecimalTypeHandler
org.apache.ibatis.type.ByteArrayTypeHandler
org.apache.ibatis.type.BlobTypeHandler
org.apache.ibatis.type.DateOnlyTypeHandler
org.apache.ibatis.type.YearMonthTypeHandler
org.apache.ibatis.type.SqlDateTypeHandler
org.apache.ibatis.type.OffsetTimeTypeHandler
org.apache.ibatis.type.LongTypeHandler
org.apache.ibatis.type.TimeOnlyTypeHandler
org.apache.ibatis.type.ZonedDateTimeTypeHandler
org.apache.ibatis.type.BlobInputStreamTypeHandler
org.apache.ibatis.type.LocalDateTypeHandler
org.apache.ibatis.type.IntegerTypeHandler
org.apache.ibatis.type.StringTypeHandler
org.apache.ibatis.type.BlobByteObjectArrayTypeHandler
org.apache.ibatis.type.ShortTypeHandler
org.apache.ibatis.type.NClobTypeHandler
org.apache.ibatis.type.LocalTimeTypeHandler
org.apache.ibatis.type.ClobReaderTypeHandler
org.apache.ibatis.type.SqlTimestampTypeHandler
org.apache.ibatis.type.SqlTimeTypeHandler
org.apache.ibatis.type.ByteObjectArrayTypeHandler
org.apache.ibatis.type.OffsetDateTimeTypeHandler
org.apache.ibatis.type.DoubleTypeHandler

Mybatis已经为我们写好了40个类型处理器

其中DateTypeHandlers的源码如下

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.apache.ibatis.type; import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date; public class DateTypeHandler extends BaseTypeHandler<Date> {
public DateTypeHandler() {
} /**
获取到对应的java类型的对象,将其转换为jdbc类型
*/
public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
ps.setTimestamp(i, new Timestamp(parameter.getTime()));
} /**
获取到对应列的jdbc类型的对象,将其转换为java类型
*/
public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
Timestamp sqlTimestamp = rs.getTimestamp(columnName);
return sqlTimestamp != null ? new Date(sqlTimestamp.getTime()) : null;
} /**
根据索引获取到对应的数据的jdbc类型将其转换为java类型
*/
public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
Timestamp sqlTimestamp = rs.getTimestamp(columnIndex);
return sqlTimestamp != null ? new Date(sqlTimestamp.getTime()) : null;
} /**
这个方法用在存储过程中。将jdbc类型转换为java类型
*/
public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
Timestamp sqlTimestamp = cs.getTimestamp(columnIndex);
return sqlTimestamp != null ? new Date(sqlTimestamp.getTime()) : null;
}
}

它的父类BaseTypeHandler<T>

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.apache.ibatis.type; import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.executor.result.ResultMapException;
import org.apache.ibatis.session.Configuration; public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
/** @deprecated */
@Deprecated
protected Configuration configuration; public BaseTypeHandler() {
} /** @Deprecated表示此方法或者类不推荐使用,但是不代表不能用*/
/** @deprecated */
@Deprecated
public void setConfiguration(Configuration c) {
this.configuration = c;
} public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
} try {
/** jdbcType是一个枚举类型 */
/** 将对应的jdbc类型设置为null */
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException var7) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. Cause: " + var7, var7);
}
} else {
try {
this.setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception var6) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . Try setting a different JdbcType for this parameter or a different configuration property. Cause: " + var6, var6);
}
} } public T getResult(ResultSet rs, String columnName) throws SQLException {
try {
return this.getNullableResult(rs, columnName);
} catch (Exception var4) {
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + var4, var4);
}
} public T getResult(ResultSet rs, int columnIndex) throws SQLException {
try {
return this.getNullableResult(rs, columnIndex);
} catch (Exception var4) {
throw new ResultMapException("Error attempting to get column #" + columnIndex + " from result set. Cause: " + var4, var4);
}
} public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
try {
return this.getNullableResult(cs, columnIndex);
} catch (Exception var4) {
throw new ResultMapException("Error attempting to get column #" + columnIndex + " from callable statement. Cause: " + var4, var4);
}
} //定义了四个抽象方法,并在上面接口方法的实现中使用了它们
//具体的实现交给子类去完成
public abstract void setNonNullParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException; public abstract T getNullableResult(ResultSet var1, String var2) throws SQLException; public abstract T getNullableResult(ResultSet var1, int var2) throws SQLException; public abstract T getNullableResult(CallableStatement var1, int var2) throws SQLException;
}

BaseTypeHandler是一个抽象类,也是类型处理器的基类。它作为TypeHandler接口的初步实现,实现了TypeHandler的四个方法;还另外定义了四个抽象方法,也就是DateTypeHandler中的四个方法。系统定义的40个TypeHandler方法都继承自BaseTypeHandler

实现的接口TypeHandler<T>

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.apache.ibatis.type; import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; public interface TypeHandler<T> {
void setParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException; T getResult(ResultSet var1, String var2) throws SQLException; T getResult(ResultSet var1, int var2) throws SQLException; T getResult(CallableStatement var1, int var2) throws SQLException;
}

如果我们要自定义typeHandler时:

  • 继承BaseTypeHandler类
  • 实现TypeHandler接口

一般不需要自定义,使用默认的就够了

映射器(mappers)

MapperRegistry:注册绑定我们的Mapper文件

方式一:

<!-- 使用相对于类路径的资源引用,在resource文件夹中 -->
<mappers>
<mapper resource="com/luoqing/dao/UserMapper.xml"/>
</mappers>

方式二:使用class文件绑定注册

<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="com.luoqing.dao.UserMapper.xml"/>
</mappers>

注意:

  • 接口和它的Mapper配置文件必须同名
  • 接口和它的Mapper配置文件必须在同一个包下

方式三:

<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="com.luoqing.dao"/>
</mappers>

注意:

  • 接口和它的Mapper配置文件必须同名
  • 接口和它的Mapper配置文件必须在同一个包下

方式四:使用映射器接口实现类的完全限定名(因为基本不使用,在此不多赘述,如要学习请移步官方文档)

作用域(Scope)和生命周期

不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder

一旦创建了 SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次。因此 SqlSessionFactory 的最佳作用域是应用作用域。最简单的就是使用单例模式或者静态单例模式。

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。下面的示例就是一个确保 SqlSession 关闭的标准模式:

try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}

在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。

Mybatis学习-配置、作用域和生命周期的更多相关文章

  1. 学习MyBatis必知必会(5)~了解myBatis的作用域和生命周期并抽取工具类MyBatisUtil、mybatis执行增删改查操作

    一.了解myBatis的作用域和生命周期[错误的使用会导致非常严重的并发问题] (1)SqlSessionFactoryBuilder [ 作用:仅仅是用来创建SqlSessionFactory,作用 ...

  2. MyBatis 作用域和生命周期

    理解到目前为止所讨论的类的作用域和生命周期是非常重要的.如果使用不当可导致严重的并发性问题. SqlSessionFactoryBuilder  这个类可以在任何时候被实例化.使用和销毁.一旦您创造了 ...

  3. Bean 注解(Annotation)配置(2)- Bean作用域与生命周期回调方法配置

    Spring 系列教程 Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of ...

  4. Bean XML 配置(2)- Bean作用域与生命周期回调方法配置

    系列教程 Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of Contro ...

  5. MyBatis(四):SqlSession及其工厂类的作用域和生命周期

    本文是按照狂神说的教学视频学习的笔记,强力推荐,教学深入浅出1便就懂!b站搜索狂神说即可 https://space.bilibili.com/95256449?spm_id_from=333.788 ...

  6. Spring之Bean的作用域与生命周期

    在前面博客中提到容器启动获得BeanDefinition对象中有一个scope 属性.该属性控制着bean对象的作用域.本章节介绍Bean的作用域及生命周期,了解bean是怎么来的又怎么没的. 一.B ...

  7. 详解Spring中Bean的作用域与生命周期

    摘要:在利用Spring进行IOC配置时,关于bean的配置和使用一直都是比较重要的一部分,同时如何合理的使用和创建bean对象,也是小伙伴们在学习和使用Spring时需要注意的部分,所以这一篇文章我 ...

  8. Spring中Bean的作用域、生命周期

                                   Bean的作用域.生命周期 Bean的作用域 Spring 3中为Bean定义了5中作用域,分别为singleton(单例).protot ...

  9. spring作用、spring注解、管理对象的作用域与生命周期、自动装配、Spring的框架包有哪些作用是什么

    Spring 1. 作用 创建和管理对象,使得开发过程中,可以不必使用new关键字创建对象,而是直接获取对象!并且,还可以通过一些配置,使得某些获取到的对象,其中某些属性已经是被赋值的! 2. Spr ...

随机推荐

  1. nacos、ribbon和feign的简明教程

    nacos简明教程 为什么需要nacos? 在微服务架构中,微服务之间经常要相互通信和调用,而且一个服务往往存在多个实例来降低负荷或保证高可用.我们假定A服务要调用B服务,最简单的方式把B服务的地址和 ...

  2. 2020最常见的200+Java面试题汇总(含答案解析)

    前言 2020年快要结束了,很多朋友问题,有没有整理今年的一些面试题,最近抽时间整理了一份Java面试题.或许这份面试题还不足以囊括所有 Java 问题,但有了它,我相信足以应对目前市面上绝大部分的 ...

  3. Git--gitLab远程仓库分支代码回退的两种方案

    事由:作为仓库的master,一时老眼昏花,把同事说的不合并看成了合并,直接合并了. 解决方法: 一.粗鲁的代码回退--直接在远程仓库合并 1. 在gitLab远程仓库中,基于想回退的代码的节点(co ...

  4. 直播APP源码是如何实现音视频同步的

    1.  音视频同步原理 1)时间戳 直播APP源码音视频同步主要用于在音视频流的播放过程中,让同一时刻录制的声音和图像在播放的时候尽可能的在同一个时间输出. 解决直播APP源码音视频同步问题的最佳方案 ...

  5. first day for my bolg

    做为一名毕业不久的兢兢业业的前端小白,傻到一直用word做笔记,还有各种手抄(捂脸),下定决心以后改用博客,据说大神们都是这么做的!嘿嘿,先把各种笔记腾上来,内容实在惨不忍睹各种智商感人,希望不要有人 ...

  6. 为什么要谨慎使用Arrays.asList、ArrayList的subList?

    1. 使用Arrays.asList的注意事项 1.1 可能会踩的坑 先来看下Arrays.asList的使用: List<Integer> statusList = Arrays.asL ...

  7. linux + MongoDB 安装 + 部署 + 讲解 (满满干货看完记得收藏噢)

    话不多说开始了! 安装 安装就依据菜鸟教程的进行安装 传送门 => https://www.runoob.com/mongodb/mongodb-linux-install.html 好啦!现在 ...

  8. IDEA与Eclipse创建struts项目

    1.IDEA创建struts项目 这里再构建struts项目是选择jar包出问题了,可以重新配置 创建页面和action配置struts.xml 启动tomcat,浏览器中运行 具体参考: https ...

  9. 这才是图文并茂:我写了1万多字,就是为了让你了解AQS是怎么运行的

    前言 如果你想深入研究Java并发的话,那么AQS一定是绕不开的一块知识点,Java并发包很多的同步工具类底层都是基于AQS来实现的,比如我们工作中经常用的Lock工具ReentrantLock.栅栏 ...

  10. Function(函数分享)第二节

    一.类型注解 1.1 类型注解 函数的类型注解分为两个部分:参数类型注解和返回值类型注解.其中返回值类型注解有时候我们可以直接省略,因为Typescript可以根据返回的语句来自动判断出返回值的类型. ...