分类:JavaEE框架技术原创文章作者: 叮当柠柠
什么是MyBatis?
Mybatis使一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。Mybatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。Mybatis可以通过简单的XML或注解来配置和映射原始类型、接口和Java POJO(Plain Old Java Objects,普通老式Java对象)为数据库中的记录。换句话也就说Mybatis是封装JDBC的一种技术,它运用了ORM的设计思想,对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。
MyBatis中的主要组成部分
Resources:资源类用于读取配置文件,在JVM加载时,只创建一次。
代码示例:
InputStreamin=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder:该类运用了建造者模式,提供了一个建造方法用于创建SqlSessionFactory工厂对象,在创建对象时需要配合Resources类获取的输入流对象来使用。这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
代码示例:
SqlSessionFactorysqlSessionFactory=newSqlSessionFactoryBuilder().builder(in);
3.SqlSessionFactory:该接口运用了工厂模式,提供一个工厂方法用于创建SqlSession对象。它是属于应用级别的,SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
代码示例:
SqlSessionsqlSession=sqlSessionFactory.openSession();
4.SqlSession:会话对象,用于封装JDBC的连接、操作、结果集,它是Mybatis的核心。每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块或用try来关闭。它有两种执行SQL语句的方式。它是属于线程级别的,它的生命周期随SqlSession的存在而存在。//1.根据SqlSession对象的方法//2.根据SQL映射接口,获取Mapper映射器,创建Mapper接口的代理实现类对象
代码示例:
publicProductqueryProductDetailByProductId(intproductId) {
try(SqlSessionsqlSession=MyBatisUtils.openSqlSession()){
//1.根据映射器中的SQL(namespace+id),查找并执行SQL
//Product product=sqlSession.selectOne("com.apesource.dao.mapper.ProductMapper.queryProductById", productId);
//2.根据SQL映射接口,获取Mapper映射器
//创建Mapper接口的代理实现类对象
ProductMapperproductMapper=sqlSession.getMapper(ProductMapper.class);
System.out.println("Mapper接口的代理实现类对象:"+productMapper);
Productproduct=productMapper.queryProductById(productId);
returnproduct;
}catch(Exceptione) {
e.printStackTrace();
returnnull;
}
}
<!-- 映射一个select查询SQL语句 -->
<!-- id: 为当前映射的SQL语句进行命名 -->
<!-- resultType : 设置查询结果使用的类型名称 -->
<selectid="queryProductById"
resultMap="productResultMap">
select * from product_info where product_id = #{pid}<!-- #{}代表动态传入的参数 -->
</select>
MyBatis配置
设置(settings)
主要作用:Mybatis框架运行规则配置
设置(settings)代码示例:
<settings>
<!-- 开启日志 -->
<settingname="logImpl"value="STDOUT_LOGGING"/>
</settings>
类型别名(typeAliases)
主要作用:设置别名
类型别名(typeAliases)代码示例:
<typeAliases>
<!-- 为每一个实体设置别名 -->
<!-- <typeAlias type="com.apesource.entity.Product" alias="Product"/> -->
<!--设置实体类包,为该package中的每个实体类自动设置别名 -->
<packagename="com.apesource.entity"/>
</typeAliases>
环境配置(environments)
主要作用:元素体中包含了事务管理和连接池的配置
环境配置(environments)代码示例:
//属性文件
driver_class=com.mysql.cj.jdbc.Driver
jdbc_url=jdbc:mysql://47.108.65.254/ApesourceAggregateData? useSSL=false&serverTimezone=UCT
db_username=apesource_stu
db_password=Apesource=1024
//配置environments
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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)
主要作用:映射器是一些绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。虽然从技术层面上来讲,任何映射器实例的最大作用域与请求它们的 SqlSession 相同。但方法作用域才是映射器实例的最合适的作用域。 也就是说,映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。 映射器实例并不需要被显式地关闭。尽管在整个请求作用域保留映射器实例不会有什么问题,但是你很快会发现,在这个作用域上管理太多像 SqlSession 的资源会让你忙不过来。 因此,最好将映射器放在方法作用域内。就像下面的例子一样:
代码示例:
public Product queryProductDetailByProductId(int productId) {
try(SqlSession sqlSession=MyBatisUtils.openSqlSession()){
//根据映射器中的SQL(namespace+id),查找并执行SQL
//Product product=sqlSession.selectOne("com.apesource.dao.mapper.ProductMapper.queryProductById", productId);
//根据SQL映射接口,获取Mapper映射器
//创建Mapper接口的代理实现类对象
ProductMapper productMapper=sqlSession.getMapper(ProductMapper.class);
System.out.println("Mapper接口的代理实现类对象:"+productMapper);
Product product=productMapper.queryProductById(productId);
return product;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
MyBatis XML 映射器
1.常用节点作用总结
select :SQL查询语句节点
<!-- 按照答题者姓名,统计该答题者的总答题数目、正确题目数目、错误题目数目 -->
<select id="countAnswerRecordDataByRespondent" resultType="map" parameterType="AnswerRecord">
SELECT COUNT(record_id) as "总数",
(SELECT COUNT(record_id) FROM answer_record WHERE right_answer=submit_answer AND respondent=#{respondent}) as "正确数",
(SELECT COUNT(record_id) FROM answer_record WHERE right_answer!=submit_answer AND respondent=#{respondent})as "错误数"
FROM answer_record
WHERE respondent=#{respondent}
</select>
update:SQL更新语句节点
<!-- 修改答题记录 -->
<!-- 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>
delete:SQL删除语句节点
+ <!-- 删除答题记录 -->
<delete id="deleteAnswerRecord" parameterType="int">
DELETE FROM answer_record WHERE record_id=#{recordId} <!--where record_id = #{id} -->
</delete>
<!-- 批量删除答题记录 -->
<delete id="deleteAnswerRecordBatch"
parameterType="list">
DELETE FROM answer_record
WHERE record_id IN
<foreach collection="list" item="rid" separator="," open="(" close=")">
#{rid}
</foreach>
</delete>
insert:SQL插入语句节点
<!--添加新答题记录 -->
<!-- parameterType:设置添加操作时使用的实体类(别名) -->
<!-- useGeneratedKeys:开启主键回填 -->
<!-- keyProperty:设置主键回填的 属性名称 -->
<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},#{submitDatetime}) -->
> VALUES(#{respondent},#{question},#{rightAnswer},#{submitAnswer},now())
> </insert>
<!-- 批量添加新答题记录 -->
<!-- foreachL用于动态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>
2.常用属性作用总结
+ id 属性:SQL语句别名
+ resultType 属性:结果集类型
parameterType 属性:传入参数类型
useGeneratedKeys属性:开启主键回填
keyProperty属性:设置主键回填的属性名称
3.常见SQL映射示例
示例1:普通增加
<insert id="insertOrder"
parameterType="Order"
useGeneratedKeys="true"
keyProperty="orderId">
insert into order_info(order_user,payment_amount,payment_time,payment_method)
values(#{orderUser},#{paymentAmount},#{paymentDateTime},#{paymentMethod})
</insert>
int insertOrder(Order order);
示例2:批量增加
<!-- 批量添加新答题记录 -->
<!-- foreachL用于动态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>
int insertAnswerRecordBatch(List<AnswerRecord> answerRecordList);
示例3:普通删除
<delete id="deleteAnswerRecord" parameterType="int">
DELETE FROM answer_record WHERE record_id=#{recordId} <!--where record_id = #{id} -->
</delete>
int deleteAnswerRecord(@Param("id")int recordId);
示例4:批量删除
<!-- 批量删除答题记录 -->
<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 insertAnswerRecordBatch(List<AnswerRecord> answerRecordList);
示例5:动态修改
<!-- 修改答题记录 -->
<!-- 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>
int updateAnswerRecord(AnswerRecord answerRecord);
示例6:动态查询
<!-- 按照条件对象中的多条件值,动态查询 -->
<!-- where节点:处理where子句和条件之间的关系 -->
<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
<!-- 按照答题者姓名,统计该答题者的总答题数目、正确题目数目、错误题目数目 -->
<select id="countAnswerRecordDataByRespondent" resultType="map" parameterType="AnswerRecord">
SELECT COUNT(record_id) as "总数",
(SELECT COUNT(record_id) FROM answer_record WHERE right_answer=submit_answer AND respondent=#{respondent}) as "正确数",
(SELECT COUNT(record_id) FROM answer_record WHERE right_answer!=submit_answer AND respondent=#{respondent})as "错误数"
FROM answer_record
WHERE respondent=#{respondent}
</select>
Map<String,Integer> countAnswerRecordDataByRespondent(String respondent);