系列内容:
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录.
简单说, MyBatis的目的是只需要程序员配置好Dao类的接口和接口的每个方法的sql语句, MyBatis自动生成Dao接口的实现类.
首先假设我们dao包中需要实现一个IUserDao的接口, api定义如下, 这个接口需要操作的是一个User类的Entity:
public interface IUserDao {
/**
* 查询所有user
* @return
*/
List<User> findAll();
}
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// getter and setter
// ...
}
接下来我们演示通过myBatis不自己写dao的实现类就让MyBatis自动建好.
SqlSessionFactory是一个SqlSession工厂, 也是MyBatis的一个非常核心的类(其实是接口), 要创建各种session和代理dao对象都需要首先有个实现了SessionFactory的实例. 下面介绍两种创建这个类的方式
基本流程如下:
1. 创建maven坐标
2. 创建实体类和dao接口
3. 创建Mybatis主配置文件
4. 创建类映射文件
创建Mybatis主配置文件
SqlConfig.xml是我们的MyBatis的主配置文件, 这个文件内容包括配置数据源和Mapper. 文件格式如下
<configuration>
<environments default="development">
<environment id="development">
<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>
<mappers>
<mapper resource="org/example/a_xml/dao/IUserDao.xml"/>
mappers>
configuration>
注意到这个核心配置xml中配置了一类叫做Mapper的标签, Mapper的目的就是告诉MyBatis要代理的Dao接口中的每个方法的sql语句是什么以及如何把结果映射成Java类.
创建类映射文件
<mapper namespace="org.org.example.a_xml.dao.IUserDao">
<select id="findAll" resultType="org.org.example.a_xml.domain.User">/*resultType 指定拿到的数据封装类*/
select * from user;
select>
mapper>
注意事项:
src/main/java/org/example/a_xml/dao/IUserDao.java
xml文件的路径: src/main/resources/org/example/a_xml/dao/IUserDao.xml
]完成上述的配置, 从上述xml文件配置构造一个SqlSessionFactory的流程如下:
public class MyBatisTest {
@Test
public void test0() throws IOException {
// 1. 读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); // 建造者模式
SqlSessionFactory factory = builder.build(in);
// 3. 使用工厂生产SqlSession对象
SqlSession session = factory.openSession(); // 工厂模式
// 4. 使用SqlSession创建Dao接口的代理
//代理模式
IUserDao userDao = session.getMapper(IUserDao.class); // 在配置的Mapper中查找代理类生成代理对象
// 5. 使用代理对象执行方法
List<User> users = userDao.findAll();
users.forEach(System.out::println);
// 6. 释放资源
session.close();
in.close();
}
}
MyBatis也提供了通过Java代码配置SqlSessionFactory的方法, 通过注解来定义映射关系, 然后直接注册到Configuration实例中
public interface IUserMapper { // 这里接口名改成了Mapper是为了和MyBatis中的概念保持一致, 接口本质没有变
/**
* 查询所有
* @return
*/
@Select("select * from user")
List<User> findAll();
/*
在Dao接口的方法上使用@select注解, 并添加select语句
同时在SqlMapConfig.xml中使用class标签指向接口
*/
}
public class MyBatisTest {
@Test
public void test1() throws IOException {
DataSource dataSource = myDataSourceFactory.getMyDataSource(); // 获得一个DataSource的实例
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(IUserMapper.class); // 注册Mapper类
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
try (SqlSession session = sqlSessionFactory.openSession()) {
IUserMapper mapper = session.getMapper(IUserMapper.class);
// 使用代理对象执行方法
List<User> users = mapper.findAll();
users.forEach(System.out::println);
}
}
}
不过,由于 Java 注解的一些限制以及某些 MyBatis 映射的复杂性,要使用大多数高级映射(比如:嵌套联合映射),仍然需要使用 XML 配置。有鉴于此,如果存在一个同名 XML 配置文件,MyBatis 会自动查找并加载它(在这个例子中,基于类路径和 BlogMapper.class 的类名,会加载 BlogMapper.xml)
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}
在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭
参考资料: