MyBatis
一、MyBatis核心组件
组件 | 作用 |
---|---|
SqlSessionFactoryBuilder | 构造器,用于构造SqlSessionFactory ,采用分布构建的Builder模式。(建造者模式) |
SqlSessionFactory | (工厂接口)使用工厂模式,生成SqlSession |
SqlSession | 可以 发送 SQL执行返回结果,也可以 获取 Mapper的接口。在使用中, SQL语句不再出现在业务逻辑代码中 ,而使用MyBatis提供的SQLMapper接口编程技术 ,从而提高代码的可读性和可维护性。 |
SQL Mapper | 映射器,由JAVA接口和XML(可用注解替代)构成,需给出对应的SQL语句和映射规则。负责发送SQL执行并返回结果 |
二、SqlSessionFactory(工厂接口)
使用MyBatis首先是:使用配置或者代码生产SqlSessionFactory,在MyBatis提供了SqlSessionFactoryBuilder方法来构造SqlSessionFactory。采用的建造者模式(Builder)来构建。
每个基于MyBatis的应用都是以一个SqlSessionFactory的实例为中心的,而SqlSessionFactory的唯一作用是:生产SqlSession,作用是唯一的。因此,通常采用单例模式处理SqlSessionFactory。
MyBatis中,生成SqlSessionFactory有如下两种方法:
- 通过读取配置的XML构建SqlSessionFactory
- 通过Java代码生成SqlSessionFactory
1. 通过读取配置的XML构建SqlSessionFactory
基础配置文件属性配置有一定的规则,否则DTD会报错,规则如下:
(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)"
<?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="jdbc.properties"/>
<!-- 设置别名 -->
<typeAliases>
<typeAlias type="edu.mju.bean.Role" alias="role" />
</typeAliases>
<!-- 数据库环境 -->
<environments default="development">
<environment id="development">
<!--这里使用的是 JDBC 的事务管理器-->
<transactionManager type="JDBC" />
<!--MyBatis内置的数据源:UNPOOLED、POOLED、JNDI-->
<dataSource type="POOLED">
<property name="driver" value="${database.driver}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</dataSource>
</environment>
</environments>
<!-- 映射文件 -->
<mappers>
<mapper resource="edu/mju/mapper/RoleMapper.xml"/>
</mappers>
</configuration>
标签 | 说明 |
---|---|
properties | 读取resource指定的配置文件 |
typeAliases | 设置别名,通过添加typeAlias标签,type为某一个类的全限定名,alias为自定义别名 |
environments | 环境配置。default参数指定使用的环境配置。 |
environment | 创建一个环境配置。 |
transactionManager | 配置事务管理器 |
dataSource | 配置数据源。MyBatis内置数据源:UNPOOLED、POOLED、JNDI。 UNPOOLED,mybaties会为每一个数据库操作创建一个新的连接,并关闭它。该方式适用于只有小规模数量并发用户的简单应用程序上。 POOLED,mybaties会创建一个数据库连接池,连接池的一个连接将会被用作数据库操作。一旦数据库操作完成,mybaties会将此连接返回给连接池。在开发或测试环境中经常用到此方式。 JNDI。mybaties会从在应用服务器向配置好的JNDI数据源DataSource获取数据库连接。在生产环境中优先考虑这种方式。 |
mappers | 引入映射文件。 |
通过以下代码,生成SqlSessionFactory
public static SqlSessionFactory getSqlSessionFactory() {
//读取配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
//通过MyBatis的Resources对象getResourceAsStream方法返回在classpath作为Stream对象的资源
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
2. 通过Java代码生成SqlSessionFactory
public static SqlSessionFactory getSqlSessionFactory() {
//数据库连接池信息
PooledDataSource dataSource = new PooledDataSource();
dataSource.setDriver("com.mysql.jdbc.Driver");
dataSource.setUrl("root");
dataSource.setPassword("123456");
dataSource.setUrl("jdbc:mysql://192.168.0.248:3306/chapter3?useUnicode=true&characterEncoding=UTF-8");
//设置是否自动提交
dataSource.setDefaultAutoCommit(false);
//配置事务管理器
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development",transactionFactory,dataSource);
//创建Configuration
Configuration configuration = new Configuration(environment);
//注册MyBatis别名
configuration.getTypeAliasRegistry().registerAlias("role", Role.class);
//注册映射器
configuration.addMapper(RoleMapper.class);
//构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
return sqlSessionFactory;
}
三、SqlSession
MyBatis中,SqlSession是核心接口。在MyBatis有两个实现类:DefaultSqlSession和SqlSessionManager。
DefaultSqlSession:在单线程使用。
SqlSessionManager:在多线程使用。
SqlSession作用:
- 获取Mapper接口
- 发送SQL给数据库
- 控制数据库事务
创建SqlSession代码如下:
public static SqlSession getSession() {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
return sqlSessionFactory.openSession();
}
//等同于
SqlSession SqlSession = SqlSessionFactory.openSession();
除此之外,SqlSession还有控制数据库事务的方法,如
- sqlSession.commit():提交事务
- sqlSession.rollback():回滚事务
四、映射器
映射器是MyBatis中最重要的组件,由一个接口和对应XML文件(或者注解)组成。
接口是不能直接运行!但MyBatis运用了动态代理技术使得接口能够运行,MyBatis为该接口生成一个代理对象,代理对象处理相关的逻辑。
1. XML文件形式实现映射器
这里我们先定义一个实体类:Role.java
package edu.mju.bean;
public class Role {
private Integer id;
private String roleName;
private String note;
//省略get/set方法
}
- 定义映射器接口
RoleMapper.java
package edu.mju.mapper;
import edu.mju.bean.Role;
public interface RoleMapper {
int insertRole(Role role);
Role selectRole(Integer id);
}
- XML创建映射器
RoleMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="edu.mju.mapper.RoleMapper">
<!-- <select id="selectRole" resultType="edu.mju.bean.Role" > -->
<select id="selectRole" resultMap="roleViewMap">
select * from t_role where id = #{id}
</select>
<insert id="insertRole" parameterType="edu.mju.bean.Role" useGeneratedKeys="true">
insert into t_role(role_name,note) values(#{roleName},#{note})
</insert>
<!-- 自定义结果集映射 -->
<resultMap id="roleViewMap" type="edu.mju.bean.Role">
<result column="id" property="id"/>
<result column="role_name" property="roleName"/>
<result column="note" property="note"/>
</resultMap>
</mapper>
属性 | 说明 |
---|---|
<mapper namaspace=" "> | namespace对应的是一个接口的全限定名,上下文通过namespace找到对应接口。 |
<resultMap id=“roleViewMap” type=“edu.mju.bean.Role”> | 自定义结果集映射。 type:指明类型为edu.mju.bean.Role类的类型。 id:唯一表标识id。 |
<result column=“role_name” property=“roleName”/> | column:数据库查询的结果列名。 property:映射的属性名。 |
insert | 映射插入语句 |
update | 映射更新语句 |
delete | 映射删除语句 |
select | 映射查询语句 |
resultMap | 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
更多属性 | 官方API |
- 配置映射器
在基础配置文件(mybatis-config.xml)中,引入映射文件
<!-- 映射文件 -->
<mappers>
<mapper resource="edu/mju/mapper/RoleMapper.xml"/>
</mappers>
2. 注解实现映射器
- RoleMapper2.java
package edu.mju.mapper;
import edu.mju.bean.Role;
import org.apache.ibatis.annotations.Insert;
public interface RoleMapper2 {
@Insert("insert into t_role(role_name,note) values(#{roleName},#{note})")
int insertRole2(Role role);
}
- 配置映射
<!-- 映射文件 -->
<mappers>
<mapper resource="edu/mju/mapper/RoleMapper.xml"/>
<mapper class="edu.mju.mapper.RoleMapper2"/>
</mappers>
3. 发送SQL
通过以上两种方法实现了映射器,可以通过SqlSession发送SQL或用Mapper接口发送SQL
- SqlSession发送SQL
/**
* 通过SqlSession发送SQL
* @param id id
* @return Role对象
* @throws IOException 异常处理
*/
public Role getByIdSqlSession(int id) throws IOException {
SqlSession session = MyBatisUtil.getSession();
//selectOne(String statement, Object parameter):
// statement:匹配要使用的语句的唯一标识符
// parameter:传递给语句的参数对象。
Role role = session.selectOne("edu.mju.mapper.RoleMapper.selectRole",id);
session.close();
return role;
}
- Mapper接口发送SQL
/**
* 通过Mapper接口发送SQL
* @param id id
* @return Role对象
* @throws IOException 异常处理
*/
public Role getByIdMapper(int id) throws IOException {
SqlSession session = MyBatisUtil.getSession();
//selectOne(String statement, Object parameter):
// statement:匹配要使用的语句的唯一标识符
// parameter:传递给语句的参数对象。
RoleMapper roleMapper = session.getMapper(RoleMapper.class);
Role role = roleMapper.selectRole(id);
session.close();
return role;
}
对比以上两种发送SQL方式,推荐使用第二种Mapper接口发送SQL的方式,优点如下:
- 使用Mapper接口编程可以消除SqlSession带来的功能性代码,提高可读性.
- 错误能够及早发现.使用第一种的方式,只能在运行中才能知道是否产生错误;而第二种方式IDE会提示错误和校验.
五、生命周期
在多线程的环境中,错误使用会造成严重的多线程并发问题,为了正确编写MyBatis的应用程序,我们需要掌握MyBatis组件的生命周期.
1. SqlSessionFactoryBuilder
SqlSessionFactoryBuilder的作用是创建SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder就失去了作用,所以它只存在于创建SqlSessionFactory的方法中,不长期存在.
2. SqlSession
SqlSessionFactory相当于数据库连接池,SqlSession相当于一个数据库连接(Connection对象).它存在于业务请求中,当业务处理完成请求后,应关闭该连接,以释放SqlSessionFactory空间,使数据库资源能够供给下一个连接对象,避免系统瘫痪.
3.Mapper
Mapper是一个接口,由SqlSession所创建,它的生命周期小于等于SqlSession的生命周期.
六、实例
1. 实体类
package edu.mju.bean;
public class Role {
private Integer id;
private String roleName;
private String note;
public Role(String roleName, String note) {
this.roleName = roleName;
this.note = note;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
2. Mapper接口
package edu.mju.mapper;
import edu.mju.bean.Role;
public interface RoleMapper {
/**
* 添加Role对象
* @param role 要添加的对象
* @return 更新的值
*/
int insertRole(Role role);
/**
* 根据id查找Role
* @param id id
* @return Role对象
*/
Role selectRole(Integer id);
/**
* 根据id删除Role
* @param id id
* @return 更新的值
*/
int deleteRole(Integer id);
/**
* 更新Role
* @param role 传入更新的Role对象
* @return 更新的值
*/
int updateRole(Role role);
}
3. XML映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="edu.mju.mapper.RoleMapper">
<!-- <select id="selectRole" resultType="edu.mju.bean.Role" > -->
<select id="selectRole" resultMap="roleViewMap">
select * from t_role where id = #{id}
</select>
<insert id="insertRole" parameterType="edu.mju.bean.Role" useGeneratedKeys="true">
insert into t_role(role_name,note) values(#{roleName},#{note})
</insert>
<delete id="deleteRole" parameterType="int">
delete from t_role where id = #{id}
</delete>
<update id="updateRole" parameterType="edu.mju.bean.Role">
update t_role set role_name = #{roleName},note = #{note} where id = #{id}
</update>
<!-- 自定义结果集映射 -->
<resultMap id="roleViewMap" type="edu.mju.bean.Role">
<result column="id" property="id"/>
<result column="role_name" property="roleName"/>
<result column="note" property="note"/>
</resultMap>
</mapper>
4. 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="jdbc.properties"/>
<!-- 设置别名 -->
<typeAliases>
<typeAlias type="edu.mju.bean.Role" alias="role" />
</typeAliases>
<!-- 数据库环境 -->
<environments default="development">
<environment id="development">
<!--这里使用的是 JDBC 的事务管理器-->
<transactionManager type="JDBC" />
<!--MyBatis内置的数据源:UNPOOLED、POOLED、JNDI-->
<dataSource type="POOLED">
<property name="driver" value="${database.driver}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</dataSource>
</environment>
</environments>
<!-- 映射文件 -->
<mappers>
<mapper resource="edu/mju/mapper/RoleMapper.xml"/>
<mapper class="edu.mju.mapper.RoleMapper2"/>
</mappers>
</configuration>
5. 工具类
用来构建SqlSessionFactory.
这里在构造方法中加入private关键字,使得其他代码不能通过new的方式来创建它.加入synchronized关键字加锁,防止多线程中多次实例化SqlSessionFactory对象,从而保证SqlSessionFactory的唯一性.
package edu.mju.utils;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MyBatisUtil {
private final static Class<MyBatisUtil> MY_BATIS_UTIL_CLASS = MyBatisUtil.class;
private static SqlSessionFactory sqlSessionFactory = null;
/**
* private修饰构造方法,不能通过new创建MyBatisUtil()。
*/
private MyBatisUtil() {
}
/**
* 创建SqlSessionFactory(工厂接口)
*
* @return SqlSessionFactory
* @throws IOException
*/
public static SqlSessionFactory getSqlSessionFactory() {
//加锁,防止多线程多次实例化SqlSessionFactory对象
synchronized (MY_BATIS_UTIL_CLASS) {
if (sqlSessionFactory != null) {
return sqlSessionFactory;
}
//读取配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
//通过MyBatis的Resources对象getResourceAsStream方法返回在classpath作为Stream对象的资源
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
}
/**
* 获取SqlSession
*
* @return SqlSession
* @throws IOException
*/
public static SqlSession getSession() {
if(sqlSessionFactory == null){
getSqlSessionFactory();
}
return sqlSessionFactory.openSession();
}
}
6. Dao处理
package edu.mju.dao;
import java.io.IOException;
import edu.mju.mapper.RoleMapper;
import org.apache.ibatis.session.SqlSession;
import edu.mju.bean.Role;
import edu.mju.utils.MyBatisUtil;
public class RoleDao {
/**
* 通过SqlSession发送SQL
* @param id id
* @return Role对象
* @throws IOException 异常处理
*/
public Role getByIdSqlSession(int id) throws IOException {
SqlSession session = MyBatisUtil.getSession();
//selectOne(String statement, Object parameter):
// statement:匹配要使用的语句的唯一标识符
// parameter:传递给语句的参数对象。
Role role = session.selectOne("edu.mju.mapper.RoleMapper.selectRole",id);
session.close();
return role;
}
/**
* 通过Mapper接口发送SQL
* @param id id
* @return Role对象
* @throws IOException 异常处理
*/
public Role getByIdMapper(int id) throws IOException {
SqlSession session = MyBatisUtil.getSession();
//selectOne(String statement, Object parameter):
// statement:匹配要使用的语句的唯一标识符
// parameter:传递给语句的参数对象。
RoleMapper roleMapper = session.getMapper(RoleMapper.class);
Role role = roleMapper.selectRole(id);
session.close();
return role;
}
/**
* 根据id删除Role
* @param id id
* @return 更新的值
*/
public int deleteRole(Integer id){
SqlSession session = MyBatisUtil.getSession();
RoleMapper roleMapper = session.getMapper(RoleMapper.class);
int result = roleMapper.deleteRole(id);
session.close();
return result;
}
/**
* 更新Role
* @param role 更新的Role对象
* @return 更新的值
*/
public int updateRole(Role role){
SqlSession session = MyBatisUtil.getSession();
RoleMapper roleMapper = session.getMapper(RoleMapper.class);
int result = roleMapper.updateRole(role);
session.close();
return result;
}
public int insertRole(Role role) throws IOException {
SqlSession session = MyBatisUtil.getSession();
//selectOne(String statement, Object parameter):
// statement:匹配要使用的语句的唯一标识符
// parameter:传递给语句的参数对象。
int i = session.insert("edu.mju.mapper.RoleMapper.insertRole",role);
session.close();
return i;
}
public int insertRole2(Role role) throws IOException {
SqlSession session = MyBatisUtil.getSession();
//selectOne(String statement, Object parameter):
// statement:匹配要使用的语句的唯一标识符
// parameter:传递给语句的参数对象。
int i = session.insert("edu.mju.mapper.RoleMapper2.insertRole2",role);
session.close();
return i;
}
}
7. 测试类
package edu.mju.test;
import java.io.IOException;
import edu.mju.bean.Role;
import edu.mju.dao.RoleDao;
public class TestDao {
public static void main(String[] args) {
Role role = getById(5);
System.out.println("------------更新role后-----------");
role.setNote("更新后");
role.setRoleName("更新后的RoleName");
updateRole(role);
getById(5);
// System.out.println(insertRole(new Role("中文测试1","中文note_1")));
//
// System.out.println(insertRole(new Role("中文测试2","中文2")));
//deleteRole(8);
}
public static Role getById(Integer id) {
RoleDao roleDao = new RoleDao();
Role role = null;
try {
//通过SqlSession发送SQL
//role = roleDao.getByIdSqlSession(id);
//通过Mapper接口发送SQL
role = roleDao.getByIdMapper(id);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(role.getId());
System.out.println(role.getRoleName());
System.out.println(role.getNote());
return role;
}
public static int insertRole(Role role) {
RoleDao roleDao = new RoleDao();
int result = 0;
try {
result = roleDao.insertRole(role);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
public static int insertRole2(Role role){
RoleDao roleDao = new RoleDao();
int result = 0;
try {
result = roleDao.insertRole2(role);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
public static void deleteRole(Integer id){
RoleDao roleDao = new RoleDao();
int i = roleDao.deleteRole(id);
System.out.println("删除Role:"+i);
}
public static void updateRole(Role role){
RoleDao roleDao = new RoleDao();
int i = roleDao.updateRole(role);
System.out.println("更新Role:"+i);
}
}