MyBatis技术总结
分类:JavaEE框架技术原创文章
-
什么是MyBatis?
MyBatis是一款优秀的持久层框架,它支持定制SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain Old Java Objects,普通老式Java对象)为数据库中的记录
MyBatis中的主要组成部分
SqlSessionFactoryBuilder(工厂构造器):它会根据配置信息或者代码生成SqlSessionFactory
SqlSessionFactory(SqlSessiion工厂):依靠工厂来生成SqlSession(会话)
SqlSession(会话):一个既可以发送SQL去执行并返回结果,也可以获取Mapper的接口
SQL Mapper(映射器):它是由一个Java接口和ML文件(或注解)构成的,需要给出对应的SQL和映射规则。它负责发送SQL语句,并返回结果SqlSessionFactory创建代码示例:
//读取配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); //创建SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSessionFactoryBuilder的生命周期:SqlSessionFactory创建成功后,SqlSessionFactoryBuilder就失去了作用,所以它只能存在于创建SqlSessionFactory的方法中,而不要让其长期存在
SqlSessionFactory的生命周期··············································································在于整个MyBatis的应用之中,一旦创建了SqlSessionFactory的生命周期就等同于MyBatis的应用周期,由于SqlSessionFactory是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据资源的控制,也会导致连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。因此在一般的应用中我们往往希望SqlSessionFactory作为一个单例,让它在应用中不共享
SqlSessionFactory的单例创建代码示例:
/* * 单例模式 */ public class MyBatisUtils { private static final String CONFIG = "mybatis-config.xml"; //配置文件 private static SqlSessionFactory sqlSessionFactory; //SqlSessionFactory对象 //类级别锁 private static final Class<MyBatisUtils> CLASS_LOCK = MyBatisUtils.class; static { //初始化 initSqlSessionFactory(); } //私有的构造方法,保证MyBatis工具类不会被实例化 private MyBatisUtils() { } private static SqlSessionFactory initSqlSessionFactory() { if (sqlSessionFactory == null) { synchronized (CLASS_LOCK) { try (InputStream inputStream = Resources.getResourceAsStream(CONFIG)) { sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } } return sqlSessionFactory; } public static SqlSession openSqlSession() { if (sqlSessionFactory == null) { initSqlSessionFactory(); } return sqlSessionFactory.openSession(); } }
SqlSession创建代码示例:
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession的生命周期:存活在一个业务请求或方法中,是线程级别的,因为每个线程都应该有它自己的 SqlSession 实例而SqlSession 的实例不是线程安全的
ps:每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
// 应用逻辑代码
}
映射器实例
映射器是一些绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。虽然从技术层面上来讲,任何映射器实例的最大作用域与请求它们的 SqlSession 相同。但方法作用域才是映射器实例的最合适的作用域。 也就是说,映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。 映射器实例并不需要被显式地关闭。尽管在整个请求作用域保留映射器实例不会有什么问题,但是你很快会发现,在这个作用域上管理太多像 SqlSession 的资源会让你忙不过来。 因此,最好将映射器放在方法作用域内。就像下面的例子一样:
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// 应用逻辑代码
}
-
MyBatis配置
- 设置(settings)
主要作用: MyBatis框架运行规则配置,会改变 MyBatis 的运行时行为
设置(settings)代码示例:
<!-- MyBatis框架运行规则配置 --> <settings> <!-- 开启日志 --> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
- 类型别名(typeAliases)
主要作用:类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余
类型别名(typeAliases)代码示例:
<!-- 设置别名(为实体类设置别名) --> <typeAliases> <!-- 为每一个实体设置别名 --> <typeAlias type="com.chen.entity.Employee" alias="Employee"/> <!-- 设置实体类包,为该package中的每个实体类自动设置别名 MyBatis会自动在包名下面搜索需要的实体类--> <package name="com.chen.entity"/> </typeAliases>
- 环境配置(environments)
主要作用:事务管理器和数据源配置,虽可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一
环境配置(environments)代码示例:
<!-- 环境列表 --> <!-- default:设置当前使用的环境environment id --> <environments default="development"> <!-- 环境1(开发) --> <environment id="development"> <!-- 事务管理器:采用JDBC事务 --> <transactionManager type="JDBC" /> <!-- 数据源(数据库连接池) --> <!-- type设置为pooled,启动数据库连接池 --> <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> <!-- 环境2(测试) --> //········· </environments>
- 映射器(mappers)
主要作用:配置映射文件资源位置
映射器(mappers)代码示例:
<!-- 映射器列表 --> <mappers> <!-- SQL映射文件 --> <mapper resource="com/chen/dao/mapper/EmployeeMapper.xml" /> <mapper resource="com/chen/dao/mapper/OrderMapper.xml" /> </mappers>
-
MyBatis XML 映射器
1.常用节点作用总结
- select : 映射查询语句
- update: 映射更新语句
- delete:映射删除语句
- insert: 映射插入语句
2.常用属性作用总结
- id 属性:在命名空间中唯一的标识符,可以被用来引用这条语句
- resultType 属性:设置结果的返回类型
- parameterType 属性:将会传入这条语句的参数类型
- useGeneratedKeys属性:(仅适用于 insert 和 update)设置为时true开启主键回填
- keyProperty属性:(仅适用于 insert 和 update)指定能够唯一识别对象的属性,即主键回填至数据表主键映射的实体类对应属性
3.常见SQL映射示例
示例1:普通增加
<insert id="insertAnswerRecord" parameterType="AnswerRecord" useGeneratedKeys="true" keyProperty="recordId"> INSERT INTO answer_record (respondent,question,right_answer,submit_answer,submit_datetime) VALUES(#{respondent},#{question},#{rightAnswer},#{submitAnswer},now()) </insert>
>/**
>> * 添加新答题记录
>> * @param answerRecord 答题记录对象
>> * @return 影响行数
>> */
>>int insertAnswerRecord(AnswerRecord answerRecord);
>>```
>```
>示例2:批量增加
>```xml
><!-- 批量添加新答题记录 -->
> <!-- foreach:用于动态SQL中的循环遍历,collection指定遍历的集合类型,
> item为每次循环遍历的元素命名 -->
> <insert id="insertAnswerRecordBatch" parameterType="list" useGeneratedKeys="true" keyProperty="recordId">
> INSERT INTO answer_record(respondent,question,right_answer,submit_answer,submit_datetime)
> VALUES
> <foreach collection="list" item="record" separator=",">
> (
> #{record.respondent},
> #{record.question},
> #{record.rightAnswer},
> #{record.submitAnswer},
> now()
> )
> </foreach>
>
> </insert>
>```
>>```java
>>/**
>> * 批量添加新答题记录
>> * @param answerRecordList 添加数据集合
>> * @return 影响行数
>> */
>>int insertAnswerRecordBatch(List<AnswerRecord> answerRecordList);
>>```
>示例3:普通删除
>```xml
><!-- 删除答题记录 -->
> <delete id="deleteAnswerRecord">
> DELETE FROM answer_record
> WHERE record_id=#{recordId}
> </delete>
>```
>>```java
>>/**
>> * 删除答题记录
>> * @param recordId 答题记录编号
>> * @return 影响行数
>> */
>>int deleteAnswerRecord(int recordId);
>>```
>示例4:批量删除
>```xml
><!-- 批量删除答题记录 -->
> <delete id="deleteAnswerRecordBatch" parameterType="list">
> DELETE FROM answer_record
> WHERE record_id IN
> <foreach collection="list" item="rid" separator="," open="(" close=")">
> #{rid}
> </foreach>
> </delete>
>```
>>```java
>>/**
>> * 批量删除答题记录
>> * @param recordIdList
>> * @return 影响行数
>> */
>>int deleteAnswerRecordBatch(List<Integer> recordIdList);
>>```
>示例5:动态修改
>```xml
><!-- 修改答题记录 -->
> <!-- set节点用于动态处理update语句中的set -->
> <!-- if节点用于条件判断 -->
> <update id="updateAnswerRecord" parameterType="AnswerRecord">
> UPDATE answer_record
> <set>
> <if test="respondent!=null">respondent=#{respondent},</if>
> <if test="question !=null"> question=#{question},</if>
> <if test="rightAnswer!=null"> right_answer=#{rightAnswer},</if>
> <if test="submitAnswer!=null"> submit_answer=#{submitAnswer},</if>
> submit_datetime=now()
> </set>
> WHERE record_id=#{recordId}
> </update>
>```
>>```java
>>/**
>> * 修改答题记录
>> * @param answerRecord 答题记录对象(包含答题记录编号)
>> * @return 影响行数
>> */
>>int updateAnswerRecord(AnswerRecord answerRecord);
>>```
>示例6:动态查询
>```xml
>SQL映射配置
>```
>>```java
>>/**
>> * 按照条件对象中的多条件值,动态查询
>> * @param condition 条件对象
>> * @return 答题记录集合
>> */
>> List<AnswerRecord> listAnswerRecordByCondition(AnswerRecord condition);
>>```
>示例7:查询结果封装为Map
>```xml
><!-- 按照答题者姓名,统计该答题者的总答题数目、正确题目数目、错误题目数目 -->
> <select id="countAnswerRecordDataByRespondent" resultType="map">
> SELECT count(record_id) as total,
> (SELECT count(record_id) FROM answer_record WHERE respondent = #{name} AND right_answer = submit_answer ) as right_count,
> (SELECT count(record_id) FROM answer_record WHERE respondent = #{name} AND right_answer != submit_answer ) as fail_count
> FROM answer_record
> WHERE respondent = #{name}
> </select>
```
```java
/**
>> * 按照答题者姓名,统计该答题者的总答题数目、正确题目数目、错误题目数目
>> * @param respondent
* @return
*/
Map<String,Integer> countAnswerRecordDataByRespondent(String respondent);
```