MyBatis是一个基于Java的数据库持久化层框架,它支持自定义 SQL、存储过程以及高级映射。它免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。 通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。MyBatis采用 ORM(Object-Relationl Mapping) 思想解决了实体和数据库映射的问题,对 JDBC进行了封装,屏蔽了 JDBC API 底层访问细节,使我 们不用与 JDBC API 打交道,就可以完成对数据库的持久化操作。
MyBatis中主要使用的类和接口有:SqlSessionFactoryBuilder、SqlSessionFactory和SqlSession。
SqlSessionFactoryBuilder负责构建SqlSessionFactory 。它可以通过XML文件构建出SqlSessionFactory示例。它的最大特点是:用过即丢。一旦创建了SqlSessionFactory 对象之后,这个类就不需要存在了,因此SqlSessionFactoryBuilder 只用存在于方法体内方法调用后就可以销毁,也就是局部变量。
SqlSessionFactory的作用就是通过openSession()方法创建出一个个的SqlSession实例。所以SqlSessionFactory实例一旦创建就伴随着整个程序的生命周期,而且在整个程序生命周期中只需要创建一个SqlSession实例即可。
SqlSessionFactory创建代码示例:
private static final String CONFIG = "mybatis-config.xml"; //配置文件
private static SqlSessionFactory sqlSessionFactory; //SqlSessionFactory对象
private static final Class<MyBatisUtils> CLASS_LOCK = MyBatisUtils.class; //类级别锁
private static SqlSessionFactory initSqlSessionFactory() {
if (sqlSessionFactory == null) {
synchronized (CLASS_LOCK) { //添加类级别同步锁保证线程安全
// 读取配置文件
try (InputStream inputStream = Resources.getResourceAsStream(CONFIG)) {
// SqlSessionFactoryBuilder通过读取到配置文件创建SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sqlSessionFactory;
}
SqlSession 是用于持久化操作的对象,类似于 JDBC 操作中的Connection 。它提供了在数据库执行 SQL 命令所需的所有方法,可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。SqlSession 的实例不是线程安全的,因此不能被共享,在每次使用都要确保它能够被关闭。
SqlSession创建代码示例:
public static SqlSession openSqlSession() {
if (sqlSessionFactory == null) {
initSqlSessionFactory();
}
// 调用sqlSessionFactory的openSession()方法创建出SqlSession实例
return sqlSessionFactory.openSession();
}
主要作用:对MyBatis运行时的行为进行设置。如MyBatis缓存、日志的开启和关闭等。
设置(settings)代码示例:
<!-- MyBatis配置 --> <settings> <!-- 开启日志 --> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
- 类型别名(typeAliases)
主要作用:类型别名可为 Java 类型设置一个缩写名字,也可以指定一个实体类的包名,MyBatis 会在包名下面搜索需要的 Java Bean。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
类型别名(typeAliases)代码示例:
<!-- 为实体类设置别名 --> <typeAliases> <!-- 为每一个实体类设置别名 type:实体类的完全限定名 alias:实体类别名 <typeAlias type="com.apesource.entity.AnswerRecord" alias="AnswerRecord"/> --> <!-- 设置实体类包,为package中的每一个实体类自动设置别名(别名为首字母小写的非限定类名) --> <package name="com.apesource.entity"/> </typeAliases>
- 环境配置(environments)
主要作用:MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有很多情况下需要使用不同的环境。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。虽然可以配置多个不同的环境,但是每次只能选择使用其中的一个环境进行使用。
环境配置(environments)代码示例:
<!-- 指定使用环境为development --> <environments default="development"> <!-- 配置一个环境id为环境“名称” --> <environment id="development"> <!-- 事务管理器的配置 --> <transactionManager type="JDBC" /> <!-- 数据源的配置 --> <dataSource type="POOLED"> <!-- 这是 JDBC 驱动的 Java 类全限定名 --> <property name="driver" value="${jdbc_driver}" /> <!-- 这是数据库的 JDBC URL 地址 --> <property name="url" value="${jdbc_url}" /> <!-- 登录数据库的用户名 --> <property name="username" value="${db_username}" /> <!-- 登录数据库的密码 --> <property name="password" value="${db_password}" /> </dataSource> </environment> </environments>
- 映射器(mappers)
主要作用:告诉 MyBatis 到哪里去找到SQL映射语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找SQL映射文件。
映射器(mappers)代码示例:
<!-- 指定SQL映射文件的位置 --> <mappers> <mapper resource="com/apesource/dao/mapper/AnswerRecordMapper.xml"/> </mappers>
1.常用节点作用总结
- select :映射一个select数据查询SQL语句。
- update:映射一个update数据更新SQL语句。
- delete:映射一个delete数据删除SQL语句。
- insert: 映射一个insert数据插入SQL语句。
2.常用属性作用总结
- id 属性: 为当前映射的SQL语句进行命名。
- resultType 属性: 设置查询结果使用的类型名称。
- parameterType 属性:设置SQL操作时使用的实体类(别名)。
- useGeneratedKeys属性: 开启主键回填。
- 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>
接口方法定义 /** * 添加新答题记录 * @param answerRecord 答题记录对象 * @return 影响行数 * ps.测试时需要输出新答题记录的编号 */ int insertAnswerRecord(AnswerRecord answerRecord);
具体方法实现 // 添加新答题记录,测试时返回主键 public int insertAnswerRecordMethod(AnswerRecord answerRecord) { // 保证sqlSession在使用完成后关闭 try(SqlSession sqlSession = MyBatisUtils.openSqlSession()) { AnswerRecordMapper answerRecordMapper =sqlSession.getMapper(AnswerRecordMapper.class); int rows = answerRecordMapper.insertAnswerRecord(answerRecord); sqlSession.commit();//提交事务 return rows; } catch (Exception e) { e.printStackTrace(); return -1; } }
示例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是指明需要遍历的集合类型,item给遍历出来的一个元素起别名,separator指明遍历出来的每个元素用什么分隔开 --> <foreach collection="list" item="record" separator=","> (#{record.respondent},#{record.question},#{record.rightAnswer}, #{record.submitAnswer},now()) </foreach> </insert>
接口方法定义 /** * 批量添加新答题记录 * @param answerRecordList 添加数据集合 * @return 影响行数 */ int insertAnswerRecordBatch(List<AnswerRecord> answerRecordList); 具体方法实现 // 批量添加新答题记录,测试时返回主键 public int insertAnswerRecordBatchMethod(List<AnswerRecord> answerRecordList) { // 保证sqlSession在使用完成后关闭 try(SqlSession sqlSession = MyBatisUtils.openSqlSession()) { AnswerRecordMapper answerRecordMapper =sqlSession.getMapper(AnswerRecordMapper.class); int rows = answerRecordMapper.insertAnswerRecordBatch(answerRecordList); sqlSession.commit();//提交事务 return rows; } catch (Exception e) { e.printStackTrace(); return -1; } }
示例3:普通删除
SQL映射配置 <!-- 删除答题记录 --> <delete id="deleteAnswerRecord"> delete from answer_record where record_id=#{recordId} </delete>
接口方法定义 >/** * 删除答题记录 * @param recordId 答题记录编号 * @return 影响行数 */ int deleteAnswerRecord(int recordId); 具体方法实现 //删除一条答题记录 public int deleteAnswerRecordMethod(int recordId) { // 保证sqlSession在使用完成后关闭 try(SqlSession sqlSession = MyBatisUtils.openSqlSession()) { AnswerRecordMapper answerRecordMapper =sqlSession.getMapper(AnswerRecordMapper.class); int rows = answerRecordMapper.deleteAnswerRecord(recordId); sqlSession.commit();//提交事务 return rows; } catch (Exception e) { e.printStackTrace(); return -1; } }
示例4:批量删除
SQL映射配置 <!-- 批量删除记录 --> <delete id="deleteAnswerRecordBatch" parameterType="list"> delete from answer_record where record_id in <foreach collection="list" item="recordId" open="(" close=")" separator=","> #{recordId} </foreach> </delete>
接口方法定义 /** * 批量删除答题记录 * @param recordIdList * @return */ int deleteAnswerRecordBatch(List<Integer> recordIdList);
具体方法实现 //批量删除 public int deleteAnswerRecordBatchMethod(List<Integer> recordIdList) { // 保证sqlSession在使用完成后关闭 try(SqlSession sqlSession = MyBatisUtils.openSqlSession()) { AnswerRecordMapper answerRecordMapper =sqlSession.getMapper(AnswerRecordMapper.class); int rows = answerRecordMapper.deleteAnswerRecordBatch(recordIdList); sqlSession.commit();//提交事务 return rows; } catch (Exception e) { e.printStackTrace(); return -1; } }
示例5:动态修改
SQL映射配置 <!-- 修改答题记录 --> <update id="updateAnswerRecord" parameterType="AnswerRecord"> <!-- set节点用于动态处理update中的set if用于处理传入的条件判断存入为空则不修改 --> 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>
接口方法定义 /** * 动态修改答题记录 * @param answerRecord 答题记录对象(包含答题记录编号) * @return 影响行数 */ int updateAnswerRecord(AnswerRecord answerRecord);
具体方法实现 //修改答题记录 public int updateAnswerRecordMethod(AnswerRecord answerRecord) { // 保证sqlSession在使用完成后关闭 try(SqlSession sqlSession = MyBatisUtils.openSqlSession()) { AnswerRecordMapper answerRecordMapper =sqlSession.getMapper(AnswerRecordMapper.class); int rows = answerRecordMapper.updateAnswerRecord(answerRecord); sqlSession.commit();//提交事务 return rows; } catch (Exception e) { e.printStackTrace(); return -1; } }
示例6:动态查询
SQL映射配置 <!-- 按照条件对象中的多条件值,动态查询 --> <select id="listAnswerRecordByCondition" resultType="AnswerRecord" parameterType="AnswerRecord"> select record_id as recordId, respondent as respondent, question as question, right_answer as rightAnswer, submit_answer as submitAnswer, submit_datetime as submitDatetime from answer_record <!-- 通过使用where节点来替代原来SQL语句中的where关键字配合if节点实现动态查询 --> <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>
接口方法定义 /** * 按照条件对象中的多条件值,动态查询 * @param condition 条件对象 * @return 答题记录集合 */ List<AnswerRecord> listAnswerRecordByCondition(AnswerRecord condition);
具体方法实现 // 按照条件对象中的多条件值,动态查询 public List<AnswerRecord> listAnswerRecordByConditionMethod(AnswerRecord condition){ // 保证sqlSession在使用完成后关闭 try(SqlSession sqlSession = MyBatisUtils.openSqlSession()) { AnswerRecordMapper answerRecordMapper =sqlSession.getMapper(AnswerRecordMapper.class); List<AnswerRecord> listAnswerRecords = answerRecordMapper.listAnswerRecordByCondition(condition); return listAnswerRecords; } catch (Exception e) { e.printStackTrace(); return Arrays.asList(); } }
示例7:查询结果封装为Map
SQL映射配置 <!-- 按照答题者姓名,统计该答题者的总答题数目、正确题目数目、错误题目数目 --> <select id="countAnswerRecordDataByRespondent" resultType="map"> SELECT COUNT(record_id) as sum_answer, (SELECT COUNT(record_id) FROM answer_record WHERE right_answer = submit_answer AND respondent = #{respondent}) as sum_true,(SELECT COUNT(record_id) FROM answer_record WHERE right_answer != submit_answer AND respondent = # {respondent}) as sum_falseFROM answer_record WHERE respondent = #{respondent} </select>
接口方法定义 /** * 按照答题者姓名,统计该答题者的总答题数目、正确题目数目、错误题目数目 * @param respondent * @return */ Map<String,Integer> countAnswerRecordDataByRespondent(String respondent);
具体方法实现 //按照答题者姓名,统计该答题者的总答题数目、正确题目数目、错误题目数目 public Map<String,Integer> countAnswerRecordDataByRespondentMethod(String respondent){ t// 保证sqlSession在使用完成后关闭 try(SqlSession sqlSession = MyBatisUtils.openSqlSession()) { AnswerRecordMapper answerRecordMapper =sqlSession.getMapper(AnswerRecordMapper.class); Map<String,Integer> mapAnswerRecordDataByRespondent = answerRecordMapper.countAnswerRecordDataByRespondent(respondent); return mapAnswerRecordDataByRespondent; } catch (Exception e) { e.printStackTrace(); return new HashMap<String,Integer>(); } }
MyBatis 更多属性和设置可参见mybatis-MyBatis 3| 配置