MyBatis技术总结

本文是对MyBatis持久层框架的全面总结,涵盖了SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession及其生命周期,以及映射器接口和XML映射器的使用。重点解析了MyBatis的主要组件和配置,包括设置、类型别名、环境配置和映射器配置。同时,通过示例展示了SQL映射的基本语法,包括增删改查操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
​	```


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值