个人建设网站流程/2023网站分享
MyBatis核心组件
持久层的概念和MyBatis的特点
持久层可以将业务数据存储到磁盘,具备长期存储能力。一般执行持久化任务的都是数据库系统,缺点是比较慢。Java互联网应用可以通过MyBatis框架访问数据库,其优势在于:
- 不屏蔽SQL,可以更精确地定位SQL语句,对其进行优化和改造;
- 提供强大、灵活的映射机制,提供动态SQL的功能,允许根据不同条件组装SQL;
- 使用Mapper的接口编程,只要一个接口和一个XML就能创建映射器,简化工作,使得开发者能集中于业务逻辑。
准备MyBatis环境
MyBatis源码和包的路径位于https://github.com/mybatis/mybatis-3/releases,
MyBatis相关的中文手册位于:https://mybatis.org/mybatis-3/zh/getting-started.html添加链接描述
下载完成后解压缩,得到目录结构如下所示:
其中jar包是MyBatis的项目工程包,lib文件目录下是MyBatis项目工程包依赖的第三方包,PDF是说明文档。
将mybatis jar包导入到自己的项目工程中,如下所示:
MyBatis的核心组件
MyBatis的核心组件分为4个部分:
- SqlSessionFactoryBuilder(构造器):它会根据配置或者代码生成SqlSessionFactory,采用的是分步构建的Builder模式;
- SqlSessionFactory(工厂接口):依靠它生成SqlSession,使用的是工厂模式;
- SqlSession(会话):一个既可以发送SQL执行返回结果,也可以获取Mapper的接口。
- SQL Mapper(映射器):由一个Java接口和XML文件(或注解)构成,需要给出对应的SQL和映射规则。它负责发送SQL去执行,并返回结果。
注意:无论是映射器还是SqlSession都可以发送SQL到数据库执行。
SqlSessionFactory(工厂接口)
使用MyBatis首先是使用配置或者代码去生成SqlSessionFactory,而MyBatis提供了构造器SqlSessionFactoryBuilder,它提供了一个类org.apache.ibatis.session.Configuration作为引导,采用的是Builder模式。具体的分步则是在Configuration类里面完成的。
在MyBatis中,既可以通过读取配置的XML文件的形式生成SqlSessionFactory,也可以通过Java代码去生成,但是这里推荐使用XML的形式,因为代码在需求修改时会比较麻烦。当配置了XML或者提供代码后,MyBatis会读取配置文件,通过Configuration类对象构建整个MyBatis的上下文。
注意:SqlSessionFactory是一个接口,在MyBatis中它存在两个实现类:SqlSessionManager和DefaultSqlSessionFactory。一般而言,具体是由DefaultSqlSessionFactory去实现的,而SqlSessionManager使用在多线程的环境中,它的具体实现依靠DefaultSqlSessionFactory。
每个基于MyBatis的应用都是以一个SqlSessionFactory的实例为中心的,而SqlSessionFactory唯一的作用就是生产MyBatis的核心接口对象SqlSession,所以它的责任是唯一的。往往采用单例模式去处理它。
使用XML构建SqlSessionFactory
首先,在MyBatis中的XML分为两类,一类是基础配置文件,通常只有一个,主要是配置一些最基本的上下文参数和运行环境;另一类是映射文件,它可以配置映射关系、SQL、参数等信息。示例文件如下:
<?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><typeAliases><!-- 别名 --><typeAlias alias="role" type="com.learn.ssm.chapter3.pojo.Role"/></typeAliases><!-- 数据库环境 --><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/chapter3"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><!-- 映射文件 --><mappers><mapper resource="com/learn/ssm/chapter3/mapper/RoleMapper.xml"/><mapper class="com.learn.ssm.chapter3.mapper.RoleMapper2"/></mappers>
</configuration>
MyBatis的基础配置文件:
- <typeAlias>元素定义了一个别名role,代表了com.learn.ssm.chapter03.pojo.Role这个类。
- <environment>元素的定义 ,这里描述的是数据库。它里面的<transactionManager>元素是配置事务管理器,这里采用的是MyBatis的JDBC管理器方式。然后采用<dataSource>元素配置数据库,其中属性type="POOLED"代表采用MyBatis内部提供的连接池方式,最后定义一些关于JDBC的属性信息。
- <mapper>元素代表引入的那些映射器。
有了基础配置文件,就可以用一段很简短的代码来生成SqlSessionFactory了。
SqlSessionFactory = SqlSessionFactory = null;
String reasource = "mybatis-config.xml";
InputStream inputStream;
try{inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch(IOException e){e.printStackTrace();
}
首先读取mybatis-config.xml,然后通过SqlSessionFactoryBuilder的Builder方法去创建SqlSessionFactory。
使用代码创建SqlSessionFactory
用代码来实现创建SqlSessionFactory示例代码:
// 数据库连接池信息
PooledDataSource dataSource = new PooledDataSource();
dataSource.setDriver("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("12345");
dataSource.setUrl("jdbc:mysql://localhost:3306/ssm");
dataSource.setDefaultAutoCommit(false);
// 采用MyBatis的JDBC事务方式
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);
// 使用SqlSessionFactoryBuilder创建SqlSessionFactory
SqlSessionFactory SqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
return SqlSessionFactory
SqlSession
在MyBatis中,SqlSession使其核心接口,在MyBatis中有两个实现类:DefaultSqlSessio和SqlSessionManager。DefaultSqlSession是单线程用的,而SqlSessionManager在多线程环境下使用。SqlSession的作用类似于一个JDBC中的Connection对象,代表着一个连接资源的启用。具体而言,它的作用有3个:
- 获取Mapper接口
- 发送SQL给数据库
- 控制数据库事务
首先查看它的创建方法:
SqlSession sqlSession = SqlSessionFactory.openSession();
注意:SqlSession只是一个门面接口,它有很多方法,可以直接发送SQL。
SqlSession控制数据库事务的方法如下:
//定义SqlSession
SqlSession sqlSession = null;
try {// 打开SqlSession会话sqlSession = SqlSessionFactory.openSession();// some code ...sqlSession.commit(); // 提交事务
} catch (Exception ex) {sqlSession.rollback(); //回滚事务
} finally {// 在finally语句中确保资源被顺利关闭if (sqlSession != null) {sqlSession.close();}
}
这里使用commit方法提交事务,或者使用rollback方法回滚事务。
映射器
映射器是MyBatis中最重要、最复杂的组件,它由一个接口和对应的XML文件(或注解)组成。它可以配置一下内容:
- 描述映射规则
- 提供SQL语句,并可以配置SQL参数类型、反射类型、缓存刷新等信息
- 配置缓存
- 提供动态SQL
这里由两种实现映射器的方式:XML文件形式和注解形式。不过在此之前,先定义一个POJO:
package chapter03.pojo;public class Role {private Long id;private String roleName;private String note;public Long getId() {return id;}public void setId(Long 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;}
}
映射器的主要作用就是将SQL查询到的结果映射为一个POJO,或者将POJO的数据插入到数据库中,并定义一些关于缓存等的重要内容。
用XML实现映射器
用XML定义映射器分为两个部分:接口和XML。先定义一个映射器接口:
package chapter03.mapper;import chapter03.pojo.Role;public interface RoleMapper {public Role getRole(Long id);
}
在用XML方式创建SqlSession的配置文件中有这样一段代码:
<mapper resource="chapter03/mapper/RoleMapper.xml"/>
它的作用就是引入一个XML文件。用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="chapter03.mapper.RoleMapper"><select id="getRole" parameterMap="long" resultType="role">select id, role_name as roleName, note from t_role where id = #{id}</select>
</mapper>
有了这两个文件,就完成了一个映射器的定义。其中的元素:
- <mapper> 元素中的属性namespace所对应的是一个接口的全限定名,于是MyBatis上下文就可以通过它找到对应的接口。
- <select>元素表明这是一条查询语句,而属性id表示了这条SQL,属性parameterType=“long”,说明传递给SQL的是一个long型的参数,而resultType="role"表示返回的是一个role类型的返回值。而role是之前配置文件mybatis-config.xml配置的别名,指代com.learn.ssm.chapter3.pojo.Role。
- #{id}表示传递进去的参数
注意:这里并没有配置SQL执行后和role的对应关系,其实这里采用的是一种称为自动映射的功能,MyBatis在默认情况下提供自动映射,只要SQL返回的列名能和POJO对应起来即可。
注解实现映射器
除了XML方式定义映射器外,还可以使用注解方式定义映射器,它只需要一个接口就可以通过MyBatis的注解来注入SQL,示例代码如下:
package chapter03.mapper;import chapter03.pojo.Role;
import org.apache.ibatis.annotations.Select;public interface RoleMapper2 {@Select("select id, role_name as roleName, note from t_role where id=#{id}")public Role getRole(Long id);
}
这里完全等同于XML方式创建映射器。但是如果它和XML文件同时定义时,XML方式将覆盖掉注解方式,所以推荐使用XML方式。另外,如果是比较复杂的SQL语句,则使用注解的方式也会非常复杂。此外,XML可以相互导入,而注解是不可以的,所以在一些比较复杂的场景下,使用XML方式会更加灵活和方便。
同理,在mybatis-config.xml文件中配置相应的XML语句:
<mapper class="chapter03.mapper.RoleMapper2"/>
也可以使用configuration对象注册这个接口,比如:
configuration.addMapper(RoleMapper2.class);
SqlSession发送SQL
有了映射器就可以通过SqlSession发送SQL了,方式如下:
Role role = (Role)sqlSession;
selectOne("chapter03.mapper.RoleMapper.getRole",1L);
selectOne方法表示使用查询并且只返回一个对象,而参数则是一个String对象和一个Object对象。这里是一个long参数,long参数是它的主键。
用Mapper接口发送SQL
SqlSession还可以获取Mapper接口,通过Mapper接口发送SQL,示例代码如下:
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = roleMapper.getRole(1L);
通过SqlSession的getMapper方法来获取一个Mapper接口,就可以调用它的方法了,因为XML文件或者接口注解定义的SQL都可以通过“类的全限定名+方法名”查找,所以MyBatis会启用对应的SQL进行运行,并返回结果。
对比两种发送SQL方式
MyBatis存在两种发送SQL的方式:一种是用SqlSession直接发送;另一种是通过SqlSession获取Mapper接口再发送。建议采用SqlSession获取Mapper的方式,理由如下:
- 使用Mapper接口编程可以消除SqlSession带来的功能性代码,提高可读性,而SqlSession发送SQL,需要一个SQL id去匹配SQL;使用Mapper接口,类似roleMapper.getRole(1L)则是完全面向对象的语言,更能体现业务的逻辑;
- 使用Mapper.getRole(1L)的方式,IDE会提示错误和检验,而使用sqlSession.selectOne(“getRole”,1L)语法,只有运行中才知道是否会陈胜错误。
生命周期
生命周期就是每一个对象应该存活的时间。
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder的作用在于创建SqlSessionFactory,创建完成后,就失去作用,即生命周期结束。
SqlSessionFactory
SqlSessionFactory可以被认为是一个数据库连接池,作用是创建SqlSession接口对象。MyBatis的本质就是Java对数据库的操作,所以SqlSessionFactory的生命周期存在于整个MyBatis的应用之中。
另外,一般的应用中,希望SqlSessionFactory作为一个单例,可以被共享。
SqlSession
SqlSession相当于一个数据库连接(Connection对象),它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给SqlSessionFactory。
Mapper
Mapper是一个接口,由SqlSession创建,所以它的最大生命周期至多和SqlSession保持一致。Mapper代表的是一个请求中的业务逻辑,所以它应该在一个请求中,一旦处理完了相关的业务,就应该废弃它。
实例
log4j.properties:日志配置文件,让后台日志数据MyBatis运行的过程日志
log4j.rootLogger=DEBUG , stdout
log4j.logger.org.mybatis=DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n
Rolo.java:POJO对象
package chapter03.pojo;public class Role {private Long id;private String roleName;private String note;public Long getId() {return id;}public void setId(Long 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;}
}
RoloMapper.java:映射器接口
package chapter03.mapper;import chapter03.pojo.Role;import java.util.List;public interface RoleMapper {public int insertRole(Role role);public int deleteRole(Long id);public int updateRole(Role role);public Role getRole(Long id);public List<Role> findRoles(String roleName);
}
RoloMapper.xml:映射器XML文件,描述映射关系、SQL等内容
<?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="chapter03.mapper.RoleMapper"><insert id="insertRole" parameterType="role">insert into t_role(role_name, note) values(#{roleName}, #{note})</insert><delete id="deleteRole" parameterType="long">delete from t_role where id= #{id}</delete><update id="updateRole" parameterType="role">update t_role set role_name = #{roleName}, note = #{note} where id= #{id}</update><select id="getRole" parameterType="long" resultType="role">select id,role_name as roleName, note from t_role where id = #{id}</select><select id="findRoles" parameterType="string" resultType="role">select id, role_name as roleName, note from t_rolewhere role_name like concat('%', #{roleName}, '%')</select>
</mapper>
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><typeAliases><!-- 别名 --><typeAlias alias="role" type="chapter03.pojo.Role"/></typeAliases><!-- 数据库环境 --><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://111.230.95.188:3306/ssm"/><property name="username" value="ssm"/><property name="password" value="5XLHKTFjSEpx"/></dataSource></environment></environments><!-- 映射文件 --><mappers><mapper resource="chapter03/mapper/RoleMapper.xml"/></mappers>
</configuration>
SqlSessionFactoryUtils.java:一个工具类,用于创建SqlSessionFactory和获取SqlSession对象
package chapter03.utils;import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;public class SqlSessionFactoryUtils {private final static Class<SqlSessionFactoryUtils> LOCK = SqlSessionFactoryUtils.class;private static SqlSessionFactory sqlSessionFactory = null;private SqlSessionFactoryUtils() {}public static SqlSessionFactory getSqlSessionFactory() {synchronized (LOCK) {if (sqlSessionFactory != null) {return sqlSessionFactory;}String resource = "chapter03/mybatis-config.xml";InputStream inputStream;try {inputStream = Resources.getResourceAsStream(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (IOException e) {e.printStackTrace();return null;}return sqlSessionFactory;}}public static SqlSession openSqlSession() {if (sqlSessionFactory == null) {getSqlSessionFactory();}return sqlSessionFactory.openSession();}
}
构造方法中加入private关键字,使得其他代码不能通过new方式创建它。而加入synchronized关键字加锁,是为了防止在多线程中多次实例化SqlSessionFactory对象,从而保证SqlSessionFactory的唯一性。而openSqlSession方法的作用则是创建SqlSession对象。
Main.java:程序入口,拥有main方法
package chapter03.main;import chapter03.mapper.RoleMapper;
import chapter03.pojo.Role;
import chapter03.utils.SqlSessionFactoryUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;public class Main {public static void main(String[] args) {Logger log = Logger.getLogger(Main.class);SqlSession sqlSession = null;try {sqlSession = SqlSessionFactoryUtils.openSqlSession();RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);Role role = roleMapper.getRole(1L);log.info(role.getRoleName());} finally {if (sqlSession != null) {sqlSession.close();}}}
}