MyBatis技术总结
分类:JavaEE框架技术原创文章
作者:幸运王
-
什么是MyBatis?
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
-
MyBatis中的主要组成部分
SqlSessionFactoryBuilder( 工厂构造器,使用了建造者模式 ) :根据配置文件来生成SqlSessionFactory
生命周期及其作用域:这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
SqlSessionFactory(SqlSession工厂,使用到了工厂模式):创建SqlSession。
生命周期及其作用域:SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。SqlSessionFactory创建代码示例:
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(会话):用于执行SQL并返回结果(创建数据库连接+创建数据库执行操作对象+返回结果集)
生命周期及其作用域:每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 try 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:SqlSession创建代码示例:
try (SqlSession session = sqlSessionFactory.openSession()) { // 应用逻辑代码 }
-
MyBatis配置
- 配置文件(properties)
主要作用:加载外部的properties文件
配置文件(properties)代码示例:
<properties resource="jdbc.properties"/>
- 设置(settings)
主要作用:MyBatis框架运行规则配置
设置(settings)代码示例:
<settings> <!-- 开启日志 --> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
- 类型别名(typeAliases)
主要作用:设置别名(为实体类设置别名),可以分别为每个实体类设置别名,也可以统一为package中的每个实体类自动设置别名
类型别名(typeAliases)代码示例:
<typeAliases> <!-- 为每一个实体设置别名 --> <!-- <typeAlias type="com.apesource.entity.Employee" alias="Employee"/> <typeAlias type="com.apesource.entity.Order" alias="Order"/> --> <!-- 设置实体类包,为该package中的每个实体类自动设置别名 --> <package name="com.apesource.entity"/> </typeAliases>
- 环境配置(environments)
主要作用:元素体中包含了事务管理和连接池的配置
环境配置(environments)代码示例:
<environments default="development"> <!-- 环境(开发) --> <environment id="development"> <!-- 事务管理器:采用JDBC事务 --> <transactionManager type="JDBC" /> <!-- 数据源(数据库连接池) --> <!-- type设置为pooled,启动数据库连接池 --> <dataSource type="POOLED"> <!-- 数据库连接参数 --> <property name="driver" value="${driver_class}" /> <property name="url" value="${jdbc_url}" /> <property name="username" value="${db_username}" /> <property name="password" value="${db_password}" /> </dataSource> </environment> </environments>
- 映射器(mappers)
主要作用:包含了一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息
映射器(mappers)代码示例:
<mappers> <!-- SQL映射文件 --> <mapper resource="com/apesource/dao/mapper/AnswerRecordMapper.xml" /> </mappers>
-
MyBatis XML 映射器
1.常用节点作用总结
- select:映射select查询SQL语句
- update:映射update修改SQL语句
- delete:映射delete删除SQL语句
- insert:映射insert增加SQL语句
2.常用属性作用总结
- id 属性:为当前映射的SQL语句进行命名,id应该与xxxMapper接口下的方法名一致
- resultType 属性:设置查询结果使用的类型名称,
- parameterType 属性:设置执行操作时使用的参数类型
- useGeneratedKeys属性:设置为true表示开启主键回填(默认为false)
- keyProperty属性:设置用于保存主键值的属性名称
3.常见SQL映射示例
示例1:普通增加
SQL映射配置 <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>
接口方法定义 int insertAnswerRecord(AnswerRecord answerRecord);
示例2:批量增加
SQL映射配置 <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>
接口方法定义 int insertAnswerRecordBatch(List<AnswerRecord> answerRecordList);
示例3:普通删除
SQL映射配置 <delete id="deleteAnswerRecord"> delete from answer_record where record_id = #{id} </delete>
接口方法定义 int deleteAnswerRecord(@Param("id")int recordId);
示例4:批量删除
SQL映射配置 <delete id="deleteAnswerRecordBatch" parameterType="list"> DELETE FROM answer_record WHERE record_id IN <foreach collection="list" item="rid" separator="," open="(" close=")"> #{rid} </foreach> </delete>
接口方法定义 int deleteAnswerRecordBatch(List<Integer> recordIdList);
示例5:动态修改
SQL映射配置 <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>
接口方法定义 int updateAnswerRecord(AnswerRecord answerRecord);
示例6:动态查询
SQL映射配置 <select id="listAnswerRecordByCondition" resultType="AnswerRecord" parameterType="AnswerRecord"> SELECT record_id AS recordId, respondent, question, right_answer AS rightAnswer, submit_answer AS submitAnswer, submit_datetime AS submitDatetime FROM answer_record <where> <if test="respondent != null">AND respondent = #{respondent}</if> <if test="question != null">AND question LIKE concat('%',#{question},'%')</if> <if test="rightAnswer != null">AND right_answer = #{rightAnswer}</if> <if test="submitAnswer != null">AND submit_answer = #{submitAnswer}</if> </where> </select>
接口方法定义 List<AnswerRecord> listAnswerRecordByCondition(AnswerRecord condition);
示例7:查询结果封装为Map
SQL映射配置 <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>
接口方法定义 Map<String,Integer> countAnswerRecordDataByRespondent(@Param("name") String respondent);