MyBatis
1 学习方法
MyBatis框架功能与Hibernate一样。
都是持久化框架。都是操作JDBC。做ORM框架。
1 MyBatis框架比Hibernate小。
2 做的是针对SQL语句查询结果进行封装。
2 MyBatis框架是什么?
MyBatis是一个针对SQL语句的ORM持久化框架。
1 针对SQL语句
在MyBatis框架中,我们要写SQL语句,针对SQL查询的结果进行封装。
2 ORM
将查询结果可以封装成一个实体对象。
3 持久化
使用JDBC进行数据库操作。
3 编写第一个MyBatis框架的工程
3.1 创建工程并导入MyBatis的jar
3.2 配置MyBatis框架
在src目录下创建mybatis.xml文件。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/t2?useUnicode=true&characterEncoding=utf8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
</configuration>
3.3 ORM映射
1 数据库表:goods_type
2 编写实体类:GoodsType
public class GoodsType {
private int typeId;
private String typeName;
3.4 编写SQL语句(写在XML文件)
在MyBatis框架中SQL语句是程序员自己编写。
在MyBatis框架时,编写的SQL语句是独立于java源文件之外。
1 编写在xml配置文件(推荐)
2 编写在方法的注解
注意:编写一个与类名同名的xml文件。
在这个xml中编写对应的CURD的SQL语句。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zpark.tea_mgr.domain.GoodsType">
<select id="findAll" resultType="com.zpark.tea_mgr.domain.GoodsType">
select * from goods_type
</select>
</mapper>
resultType="com.no1.domain.GoodsType"
表示查询结果的每一条记录封装成指定的类型。
(只有在字段名称与属性名称一致时可用)
3.5 在MyBatis配置文件中注册SQL的XML文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">……</environments>
<mappers>
<mapper resource="com/zpark/tea_mgr/domain/GoodsType.xml" />
</mappers>
</configuration>
3.6 获得一个SqlSession的实例
在MyBatis中使用核心组件SqlSession进行CRUD操作。
@Before
public void before() {
try {
String resource = "mybatis.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
this.sqlSession = ssf.openSession();
} catch (IOException e) {
e.printStackTrace();
}
}
3.7 通过SqlSession组件的方法调用CRUD
查询所有
public void test01() {
try {
List<GoodsType> goodsTypeList = this.sqlSession.selectList("com.zpark.tea_mgr.domain.GoodsType.findAll");
for (GoodsType goodsType : goodsTypeList) {
System.out.println(goodsType);
}
} catch(Exception e) {
e.printStackTrace();
} finally {
this.sqlSession.close();
}
}
4 配置本地DTD文件
Q:为什么要配置DTD文件?
DTD相当于是编写XML文件的代码提示蓝本,在XML文件中声明DTD的ID或者URL,再在MyEclipse或者Eclipse中配置一下,重新打开这个XML文件,就可以用alt+/获得像编写JAVA代码一样的提示功能。具体的配置方法可以百度一下~
当然,这个文件主要的功能是验证XML文件编写的合法性,也就是一个约束,要求你只能按DTD定义的格式写。
建议去看一下XML相关知识。
4.1 找到本地dtd文件
4.2 在工具中绑定
5 当表中字段名称与实体类中属性名称不一致
5.1 创建数据库表 goods
5.2 创建实体类
5.3 编写Goods.xml文件
5.4 在mybatis.xml文件中注册Goods.xml文件
5.5 完成CRUD并测试
6 使用Mapper接口开发MyBatis项目
在实际开发中,不直接使用SqlSession的实例,而是使用Mapper接口,构建dao层。
6.1 Mapper接口是什么
映射器是你创建的绑定映射语句的接口(interface)
映射器接口的实例可以从SqlSession中获得
6.2 创建工程并导入jar
6.3 编写实体类
public class Goods {
// goods_id int
private int goodsId;
// goods_name varchar
private String goodsName;
// goods_price double
private double goodsPrice;
// goods_num int
private int goodsNum;
// goods_type int
private int goodsType;
}
6.4 编写Mapper接口
Mapper接口相当于Hebernate中的DAO
在这个接口中声明针对Goods这个实体类进行哪些CRUD操作
public interface GoodsMapper {
public List<Goods> findAll();
}
6.5 编写GoodsMapper接口的实现类
在MyBatis框架中,接口的实现类是一个XML文件
在GoodsMapper接口的同目录创建一个叫GoodsMapper.xml文件
<mapper namespace="com.zpark.tea_mgr.mapper.GoodsMapper">
<select id="findAll" resultMap="resultGoods">
select * from goods
</select>
<resultMap type="com.zpark.tea_mgr.domain.Goods" id="resultGoods">
<id property="goodsId" column="goods_id" javaType="int" />
<result property="goodsName" column="goods_name" javaType="java.lang.String" />
<result property="goodsPrice" column="goods_price" javaType="double" />
<result property="goodsNum" column="goods_num" javaType="int" />
<result property="goodsType" column="goods_type" javaType="int" />
</resultMap>
</mapper>
6.6 在mybatis.xml文件中注册GoodsMapper.xml文件
<configuration>
<environments default="development">……</environments>
<mappers>
<mapper resource="com/zpark/tea_mgr/mapper/GoodsMapper.xml" />
</mappers>
</configuration>
6.7 从SqlSession中获得Mapper接口的实例
GoodsMapper goodsMapper = this.sqlSession.getMapper(GoodsMapper.class);
7 带参数的查询
7.1 只有一个参数时
只有一个参数时,在xml文件中只要制定类型就可以
在sql语句中可以使用任意名称的参数
public Goods findById(int id);
<select id="findById" parameterType="int" resultMap="resultGoods">
select * from goods
where goods_id = #{id}
</select>
7.2 多个参数时
按价格区间查询的方法
public List<Goods> findByPrice(
@Param("minPrice") double minPrice, @Param("maxPrice") double maxPrice);
<select id="findByPrice" resultMap="resultGoods">
select * from goods
where goods_price between #{minPrice} and #{maxPrice}
</select>
这个时候不用写parameterType
参数
8 使用对象作为参数
8.1 保存商品
public void save(Goods goods);
<insert id="save" parameterType="com.zpark.tea_mgr.domain.Goods">
insert into goods(goods_id, goods_name, goods_price, goods_type)
values(#{goodsId}, #{goodsName}, #{goodsPrice}, #{goodsType})
</insert>
try {
GoodsMapper goodsMapper = this.sqlSession.getMapper(GoodsMapper.class);
Goods goods = new Goods();
goods.setGoodsName("测试用商品A");
goods.setGoodsPrice(6666);
goods.setGoodsNum(66);
goods.setGoodsType(4);
goodsMapper.save(goods);
this.sqlSession.commit();
System.out.println("新增商品成功");
} catch(Exception e){
e.printStackTrace();
this.sqlSession.rollback();
throw new RuntimeException(e);
} finally{
this.sqlSession.close();
}
8.2 多条件查询的对象
public class GoodsSearchVO {
private String goodsName;
private double minPrice;
private double maxPrice;
……
public List<Goods> findBySearchVO(GoodsSearchVO goodsSearchVO);
<select id="findBySearchVO"
parameterType="com.zpark.tea_mgr.vo.GoodsSearchVO" resultMap="resultGoods">
select * from goods
where goods_name like concat('%', concat(#{goodsName}, '%'))
and goods_price between #{minPrice} and #{maxPrice}
</select>
try {
GoodsMapper goodsMapper
= this.sqlSession.getMapper(GoodsMapper.class);
GoodsSearchVO goodsSearchVO = new GoodsSearchVO("球", 100, 300);
List<Goods> goodsList
= goodsMapper.findBySearchVO(goodsSearchVO);
for (Goods goods : goodsList) {
System.out.println(goods);
}
} finally{
this.sqlSession.close();
}
8.3 使用对象封装聚合查询的结果 GoodsGroupVO
查询所有的商品的最大价格和最小价格
public class GoodsGroupVO {
private double minPrice;
private double maxPrice;
public GoodsGroupVO findByGroup();
<select id="findByGroup" resultType="com.zpark.tea_mgr.vo.GoodsGroupVO">
select
min(goods_price) as 'minPrice',
max(goods_price) as 'maxPrice'
from goods
</select>
9 MyBatis中的分页查询
9.1 SQL语句的分页查询
MySQL: limit 索引, 个数
Oracle: 二层子查询 + rownum
9.2 导入分页插件的jar文件
需要使用的jar文件
项目中的jar包
9.3 在MyBatis中注册分页的插件
在mybatis.xml文件中注册插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql" />
</plugin>
</plugins>
9.4 使用分页插件
分页插件使用一个静态方法设置
有效范围: 进队后面的第一个查询有效
PageHelper.startPage(1, 2);
List<Goods> goodsList = goodsMapper.findAll();
PageHelper.startPage(int pageNum, int size);
1、 int pageNum
当前页码, 从 1 开始
2、 int size
每页记录数量
9.5 获得分页信息
比如当前页吗,总记录数量等
通过这些信息进行页面分页控制。 上一页, 下一页
在分页插件中提供了一个叫PageInfo
的类,这个类封装了分页的相关信息
通过构造方法装载分页的集合对象
PageInfo<Goods> pageInfo = new PageInfo<Goods>(goodsList);
分页相关信息的方法使用
try{
GoodsMapper goodsMapper
= this.sqlSession.getMapper(GoodsMapper.class);
PageHelper.startPage(1, 2);
List<Goods> goodsList = goodsMapper.findAll();
PageInfo<Goods> pageInfo = new PageInfo<Goods>(goodsList);
for (Goods goods : pageInfo.getList()) {
System.out.println(goods);
}
System.out.println("当前页码:" + pageInfo.getPageNum());
System.out.println("每页记录数:" + pageInfo.getPageSize());
System.out.println("总页码数:" + pageInfo.getPages());
System.out.println("总记录数:" + pageInfo.getTotal());
System.out.println("上一页:" + pageInfo.getPrePage());
System.out.println("下一页:" + pageInfo.getNextPage());
} finally{
this.sqlSession.close();
}
注意控制上一页,下一页合理范围(上一页不会 <= 0, 下一页不会 超过总页码),相关的if语句自己完成
10 关联关系映射
在MyBatis的框架中可以进行关联关系的映射,但是种类没有Hibernate那么多
10.1 只有二类关联关系
第一类: 对一 有一个实体类需要映射
第二类: 对多 有一个集合需要映射
11 对一的关联映射
11 .1数据库中关联关系
11.2 实体对象之间的关系
现在只处理对一的情况,所以在Goods类中有一个GoodsType类的对象
public class GoodsType {
private int typeId;
private String typeName;
public class Goods {
private int goodsId;
private String goodsName;
private double goodsPrice;
private int goodsNum;
private GoodsType goodsType;
Goods
类最后一个属性 设计为GoodsType
类型,表明对一关系
11.3 编写Mapper接口
public interface GoodsMapper {
public List<Goods> findAll();
11.4 编写XML文件
在这个XML文件中配置关联对象的映射,在MyBatis中有三种解决方案可选
11.4.1 第一种
直接通过对象打点调用属性的方式做映射
要求: 查询语句要使用连接查询
<!-- 第一种方案:使用连接查询 -->
<select id="findAll" resultMap="resultGoods">
select * from goods
left join goods_type
on goods_type.typeId = goods.goods_type
</select>
<resultMap type="com.zpark.tea_mgr.domain.Goods" id="resultGoods">
<id property="goodsId" column="goods_id" javaType="int" />
<result property="goodsName" column="goods_name" javaType="java.lang.String" />
<result property="goodsPrice" column="goods_price" javaType="double" />
<result property="goodsNum" column="goods_num" javaType="int" />
<!-- 第一种方案:直接使用打点调用属性的方式 -->
<result property="goodsType.typeId" column="typeId" javaType="int" />
<result property="goodsType.typeName" column="typeName" javaType="java.lang.String" />
</resultMap>
11.4.2 第二种
引用关联关系对象的Mapper映射
GoodsTypeMapper.xml文件
<resultMap type="com.zpark.tea_mgr.domain.GoodsType" id="resultGoodsType">
<id property="typeId" column="typeId" javaType="int" />
<result property="typeName" column="typeName"
javaType="java.lang.String" />
</resultMap>
GoodsMapper.xml文件
<association property="goodsType"
javaType="com.zpark.tea_mgr.domain.GoodsType" resultMap="com.zpark.tea_mgr.mapper.GoodsTypeMapper.resultGoodsType" />
11.4.3 第三种
调用关联关系对象的Mapper的查询方法
public interface GoodsTypeMapper {
public GoodsType findById(int typeId);
<select id="findById" parameterType="int" resultMap="resultGoodsType">
select * from goods_type where typeId = #{id}
</select>
GoodsMapper.xml中
<!-- 第三种方案: 查自己就好了 -->
<select id="findAll" resultMap="resultGoods">
select * from goods
</select>
<!-- 第三种方案: 使用关联关系对象的查询的方法 -->
<association property="goodsType"
javaType="com.zpark.tea_mgr.domain.GoodsType"
select="com.zpark.tea_mgr.mapper.GoodsTypeMapper.findById"
column="goods_type" />
12 对多的关联映射
一个类型有多个商品
设计为: 在类型类中有一个商品的集合
12.1 编写实体类
public class GoodsType {
private int typeId;
private String typeName;
private List<Goods> goodsList;
12.2 编写GoodsTypeMapper接口
public interface GoodsTypeMapper {
public List<GoodsType> findAll();
12.3 编写XML文件
映射从一个类型到多个商品
12.3.1 第一种,连接查询所有商品信息
<select id="findAll" resultMap="resultGoodsType">
select * from goods_type
left join goods
on goods.goods_type = goods_type.typeId
</select>
<resultMap type="com.zpark.tea_mgr.domain.GoodsType" id="baseGoodsType">
<id property="typeId" column="typeId" javaType="int" />
<result property="typeName" column="typeName"
javaType="java.lang.String" />
</resultMap>
<resultMap type="com.zpark.tea_mgr.domain.GoodsType" id="resultGoodsType"
extends="baseGoodsType">
<collection property="goodsList" javaType="java.util.List"
ofType="com.zpark.tea_mgr.domain.Goods"
resultMap="com.zpark.tea_mgr.mapper.GoodsMapper.resultGoods"
/>
</resultMap>
12.3.2 第二种 调用关联关系对象的Mapper的查询方法
public interface GoodsMapper {
public List<Goods> findByType(int goodsTypeId);
<select id="findByType" parameterType="int" resultMap="resultGoods">
select * from goods where goods_type = #{id}
</select>
<!-- 第二种方案 -->
<collection property="goodsList" javaType="java.util.List"
ofType="com.zpark.tea_mgr.domain.Goods"
select="com.zpark.tea_mgr.mapper.GoodsMapper.findByType"
column="typeId" />
13 开发环境应用方案
13.1 不要使用双向映射
13.2 针对一个对象的映射,使用连接查询
13.3 针对一个集合的映射,不要再MyBatis的Mapper文件中映射
** 但要提供查询的方法,在业务层完成组装**
14 映射案例 ---- 最佳解决方案
14.1 编写实体类
public class Goods implements Serializable{
private static final long serialVersionUID = 8874153057031505335L;
private Integer goodsId;
private String goodsName;
private Double goodsPrice;
private Integer goodsNum;
private GoodsType goodsType;
public class GoodsType {
private Integer typeId;
private String typeName;
private List<Goods> goodsList;
14.2 编写Mapper接口
public interface GoodsMapper {
public List<Goods> findAll();
/**
* 给关联关系对象(商品类型)提供查找其下商品的方法
* 如果要查某个商品类型信息的时候要带出相关的商品信息
* @param typeId 某个商品类型的ID
* @return 返回某个商品类型下的商品集
*/
public List<Goods> findByType(int typeId);
public interface GoodsTypeMapper {
public List<GoodsType> findAll();
public GoodsType findById(int typeId);
14.3 编写XML文件
14.3.1 编写GoodsMapper.xml文件
针对对象在编写查询语句时,直接使用连接查询
<select id="findAll" resultMap="BaseResultMap">
select * from goods
left join goods_type
on goods_type.typeId = goods.goods_type
</select>
<select id="findByType" parameterType="java.lang.Integer"
resultMap="BaseResultMap">
select * from goods
left join goods_type
on goods_type.typeId = goods.goods_type
where goods_type = #{id}
</select>
编写映射时直接关联关系对象的resultMap映射
<resultMap type="com.zpark.tea_mgr.domain.Goods" id="BaseResultMap">
<id column="goods_id" property="goodsId" jdbcType="INTEGER" />
<result column="goods_name"
property="goodsName" jdbcType="VARCHAR" />
<result column="goods_price" property="goodsPrice" jdbcType="DOUBLE" />
<result column="goods_num" property="goodsNum" jdbcType="INTEGER" />
<association property="goodsType"
javaType="com.zpark.tea_mgr.domain.GoodsType" resultMap="com.zpark.tea_mgr.mapper.GoodsTypeMapper.BaseResultMap" />
</resultMap>
14.3.2 编写GoodsTypeMapper.xml文件
针对集合,不用连接查询集合的数据
<select id="findAll" resultMap="BaseResultMap">
select * from goods_type
</select>
<select id="findById" parameterType="java.lang.Integer"
resultMap="BaseResultMap">
select * from goods_type
where typeId = #{id}
</select>
针对集合映射时,不要映射集合
<resultMap type="com.zpark.tea_mgr.domain.GoodsType" id="BaseResultMap">
<id property="typeId" column="typeId" javaType="java.lang.Integer" />
<result column="typeName" property="typeName" jdbcType="VARCHAR" />
</resultMap>
14.4 编写service层
public class GoodsTypeService {
public GoodsType findById(int typeId){
SqlSession sqlSession = null;
try{
String resource = "mybatis.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
sqlSession = ssf.openSession();
GoodsTypeMapper goodsTypeMapper =
sqlSession.getMapper(GoodsTypeMapper.class);
GoodsType goodsType = goodsTypeMapper.findById(typeId);
GoodsMapper goodsMapper =
sqlSession.getMapper(GoodsMapper.class);
List<Goods> goodsList = goodsMapper.findByType(typeId);
// 组装
goodsType.setGoodsList(goodsList);
return goodsType;
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException(e);
}finally{
sqlSession.close();
}
}
}
14.5 测试
15 动态SQL
15.1 set标签在update语句中的使用
public interface GoodsMapper {
public void update(Goods goods);
public Goods findById(int goodsId);
public void updateByDynamicSQL(Goods goods);
<update id="updateByDynamicSQL"
parameterType="com.zpark.tea_mgr.domain.Goods">
update goods
<set>
<if test="goodsName != null">
goods_name = #{goodsName},
</if>
<if test="goodsPrice != null">
goods_price = #{goodsPrice},
</if>
<if test="goodsNum != null">
goods_num = #{goodsNum},
</if>
<if test="goodsType != null and goodsType.typeId != null">
goods_type = #{goodsType.typeId},
</if>
</set>
where goods_id = #{goodsId}
</update>
使用动态SQL,我们要修改商品,可以不必先从数据库中查询出来
15.2 where标签在查询语句中的使用
public class GoodsSearchVO {
private String goodsName;
private Double minPrice;
private Double maxPrice;
public List<Map<String, Object>> findBySearchVO(GoodsSearchVO goodsSearchVO);
<select id="findBySearchVO"
parameterType="com.zpark.tea_mgr.vo.GoodsSearchVO"
resultType="java.util.Map">
select
goods_name as 'goodsName',
goods_price as 'goodsPrice',
goods_num as 'goodsNum',
typeName as 'typeName'
from goods
left join goods_type
on goods_type.typeId = goods.goods_type
<where>
<if test="goodsName != null">
goods_name like concat('%', #{goodsName}, '%')
</if>
<if test="minPrice != null">
and goods_price > #{minPrice}
</if>
<if test="maxPrice != null">
and goods_price < #{maxPrice}
</if>
</where>
</select>
15.3 foreach标签的使用
批量插入
public void saveBat(List<Goods> goodsList);
<insert id="saveBat" parameterType="java.util.List">
insert into goods (goods_name, goods_price, goods_num, goods_type)
values
<foreach collection="list" item="goods" separator=",">
(#{goods.goodsName}, #{goods.goodsPrice}, #{goods.goodsNum},
#{goods.goodsType.typeId})
</foreach>
</insert>
批量删除
public void delBat(List<Integer> seq);
<delete id="delBat" parameterType="java.util.List">
delete from goods
where goods_id in
<foreach collection="list" item="id" separator="," open="(" close=")"
index="index">
#{id}
</foreach>
</delete>
16 MyBatis 整合 Spring
16.1 Spring 整合MyBatis框架时
1.spring抢MyBatis的连接
2 Spring抢Mapper接口
3 spring抢Mapper.xml文件
16.2 创建工程并导入jar包
导入MyBatis框架
导入Spring框架
16.3 Spring抢MyBatis的连接
1 MyBatis的配置文件中不用写连接参数
2 在applicationContext.xml文件中配置数据源
3配置一个MyBatis的sqlSessionFactory
16.4 Spring抢Mapper.xml文件
16.5 Spring配置事务处理对象
16.6 编写实体类
16.7 编写Mapper接口
@Repository("GoodsMapper")
public interface GoodsMapper {
public List<Goods> findAll();
public void save(Goods goods);
}
Mapper.xml文件没有什么改动,这里不再提供了
@Repository("GoodsTypeMapper")
public interface GoodsTypeMapper {
16.8 编写业务层 GoodsService
@Service("GoodsService")
public class GoodsService {
@Autowired
@Qualifier("GoodsMapper")
private GoodsMapper goodsMapper;
public GoodsMapper getGoodsMapper() {
return goodsMapper;
}
public void setGoodsMapper(GoodsMapper goodsMapper) {
this.goodsMapper = goodsMapper;
}
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public List<Goods> findAll(){
try{
return this.goodsMapper.findAll();
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException(e);
}
}
@Transactional(propagation = Propagation.REQUIRED,
rollbackFor = { Exception.class })
public void save(Goods goods){
try{
this.goodsMapper.save(goods);
System.out.println("保存成功!");
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
16.9 测试
Albert.Zhou.CH总结 前辈经验于此,与各位分享
2019-03-20
-------------------------------To be continued------------------------------