MyBatis框架2

目录

一、Sql编写高级特性-批量增删

1.SQL-foreach-批量删除

1.1.批量删除方法

 1.2编写SQL

1.3.编写测试

2.动态SQL-foreach-批量插入

2.1.映射器批量插入方法

2.2.编写SQL

2.3.编写测试

二、Sql编写高级特性-多对一

1.多对一保存

1.1.创建员工部门Domain

1.2.创建员工部门表

1.3.部门DeptMapper映射器

1.4.编写DeptMapper.xml映射文件

1.5.员工EmployeMapper映射器

1.6.员工EmployeeMapper.xml映射文件

3多对一查询-嵌套结果

4多对一查询-嵌套查询

5懒加载配置

三.Sql编写高级特性-一对多

1.一对多保存

四.缓存的使用

1.一级缓存

2.二级缓存


一、Sql编写高级特性-批量增删

当我们在删除多条数据,或者添加多条数据时,我们可能会选择for循环的方式调用mapper逐条删除或添加,如果有N条数据你就会调用N次mapper,这样的代码无疑是非常消耗性能的,我们可以通过MyBatis的动态SQL foreach来实现批量删除和批量添加,调用一次mapper就能添加n条数据,提升性能。

1.SQL-foreach-批量删除

有的时候我们需要对数据进行批量操作,如根据多个ID查询,或者根据多个ID删除,我们可以通过 in(1,2) 来实现,编写如下SQL

SELECT * from employee where id in (1,2);    //批量查询
DELETE   from employee where id in (3,4)    //批量删除

那么在MyBastis怎么批量删除呢?

1.1.批量删除方法

批量删除方法,参数是一个LIST,即:根据多个ID来删除。

public interface EmployeeMapper {
    //...
    int batchDelete(List<Long> ids);
}

 1.2编写SQL

<delete id="batchDelete" parameterType="list">
        DELETE FROM employee
        where id in
        <foreach collection="list" open="(" item="id" separator="," close=")" >
            #{id}
        </foreach>
    </delete>

这里稍微麻烦,我们的目的是要把 list参数中的多个 id值 动态的拼接成 where id in (1,2); 这种效果,这里用到了 foreach 循环

  • collection 指的是集合或者数组,这里接受两种值,如果是集合就写 “list”,如果是数组就写“array”

  • open 开始元素,我们需要使用“( ” 作为开始

  • item 循环的每一个元素,这里的每一个元素就是list中的每一个id

  • separator 分隔符,我们拼接后的sql需要使用“,”来分割多个ID

  • close 结束元素,我们需要使用“ )”作为结束

  • #{id} 循环的内容,这里是把id的值取出来了,比如循环三次就如同 : (#{id}#{id}#{id})

根据上面的配置,这个循环会在最前面加上 "(" , 后面加上“)” , 然后取出每个item的值即ID ,然后使用分隔符“,”进行分割,最终形成 (1,2) 这种效果。

1.3.编写测试

 

@Test
    public void batchDelete() {
        try(SqlSession sqlSession = MyBatisUtil.openSession()){
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            employeeMapper.batchDelete(Arrays.asList(11L,12L));
            sqlSession.commit();
        }
    }

2.动态SQL-foreach-批量插入

批量插入和批量删除都可以使用foreach来实现

2.1.映射器批量插入方法

public interface EmployeeMapper {
    int batchInsert(List<Employee> employees);
    //....
}

2.2.编写SQL

我们来回顾一下我们以前批量insert的SQL语法

 INSERT INTO employee(username,age,sex) VALUES("ls",11,1),("ww",12,1),("cq",13,1)

那么这个语法在MyBatis中怎么实现呢?修改SQL映射文件,添加SQL:  

<insert id="batchInsert" parameterType="list">
        INSERT INTO employee(username,age,sex) VALUES
        <foreach collection="list" item="emp" separator=",">
            (#{emp.username},#{emp.age},#{emp.sex})
        </foreach>
    </insert> 

这里跟批量删除有点不一样 ,我们需要的效果是 VALUES ("ls",11,1),("ww",12,1),("cq",13,1) 这种,你观察它其实没有开始符号和结束符号,因为每个值前面都有 ”(“以及”)“ ,所以不能使用 open和close 。这里的循环内容为 (#{emp.username},#{emp.age},#{emp.sex}) ,比如循环两次,用“ , ”分割就是:

(#{emp.username},#{emp.age},#{emp.sex}) , (#{emp.username},#{emp.age},#{emp.sex}) 效果。这里的item的值是emp,其实是一个Employee对象,因为list中装的就是Employee对象,当 #{xx}被替换成对应的值之后就形成了VALUES ("ls",11,1),("ww",12,1)这种效果

2.3.编写测试

 

 @Test    
public void batchInsert() {        
try(SqlSession sqlSession = MyBatisUtil.openSession()){          
  EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);    
        //批量插入           
                 employeeMapper.batchInsert(Arrays.asList(                    
                        new Employee("ww",11,true),
                        new Employee("cq",12,true), 
                        new Employee("zl",13,true)
                ));              
                  sqlSession.commit();       
       }  
  }

二、Sql编写高级特性-多对一

对象关联关系理解

  • 一对一:一个用户对应一个身份证号 , 一个支付宝账号对应一个余额宝账号

    任意 一方放一个外键

  • 多对一:多个员工属于同一个部门

    在多方设计一个外键关联就OK

  • 一对多:一个部门下面有多个员工

    还是在多方设计一个外键来处理

  • 多对多:一个学生可以有多个老师,一个老师也可以有多个学生 。站在任何一方思考都是一对多,那就是多对多。



    关联查询方式-ResultMap(关联映射) 

 

什么叫做关联查询!就是我们在查询数据的时候,把关联对象一起查询出来。 比如查询员工的时候要查询他所对应部门信息,首先来说查询出来要有对象来放。所以需要设计关联对象类来访,案例如下

public class Employee

{

//自己信息

private Long id;

private String name;

private Department dept;

}

查询方式有两种:

-- 方案1:嵌套查询,发送N+1条,效率低

select * from t_employee -- where id =1; select * from t_department where id =1

-- 方案2:嵌套结果 一条sql,效率高 SELECT e.*, d.id did, d.NAME dname FROM t_employee e LEFT JOIN t_department d ON e.dept_id = d.id

1.多对一保存

1.1.创建员工部门Domain

建立对象之间的关系,Employee中包含Dept对象  

public class Dept {
    private Long id;
    private String name;
    private String sn;

//--------------------    

public class Employee {

    private Long id;
    private String username;
    private String password;
    private Integer age;
    //关系:多对一
    private Dept dept;

    public Employee() {
    }

    public Employee(String username, String password, Integer age, Dept dept) {
        this.username = username;
        this.password = password;
        this.age = age;
        this.dept = dept;
    }

1.2.创建员工部门表

建立表之间的关系,employee中有detp_id外键ID  

CREATE TABLE `dept` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `sn` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;


--------------------------------------------

CREATE TABLE `employee` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `dept_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

 

1.3.部门DeptMapper映射器

编写DeptMapper,insert方法  

 public interface DeptMapper {
    void insert(Dept dept);
}

1.4.编写DeptMapper.xml映射文件

编写DeptMapper.xml,insert方法  

<?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">

<!--
    namespace:命名空间 ,sql坐标(SQL在哪儿)
-->
<mapper namespace="cn.lzh.mapper.DeptMapper">

    <insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        INSERT INTO dept(name,sn) values (#{name},#{sn})
    </insert>
</mapper>

 

1.5.员工EmployeMapper映射器

EmployeMapper,insert方法  


public interface EmployeeMapper {
    void insert(Employee employee);

1.6.员工EmployeeMapper.xml映射文件

编写EmployeeMapper.xml的insert方法的SQL  

 <!--    1.多对一保存:#{dept.id} 取员工对象中关联的的 dept对象的id属性-->
<insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
    INSERT INTO
    employee(username,password,age,dept_id)
    VALUES
    (#{username},#{password},#{age},#{dept.id})
</insert>

 

2.多对一修改

 <update id="updateById" parameterType="cn.lzh.mybatis._03_many_2_one.domain.Employee">
    update employee
    <set>
        <if test="username != null and username != ''">
            username = #{username}
        </if>
        <if test="dept != null and dept.id != null">
            dept_id = #{dept.id}
        </if>
    </set>
</update>

 

3多对一查询-嵌套结果

  <select id="selectAll" resultType="cn.lzh.mybatis._03_many_2_one.domain.Employee">
        select id,username,age,sex,dept_id
        from employee
    </select> 

<select id="selectAll" resultType="cn.lzh.mybatis._03_many_2_one.domain.Employee">
        select
            e.id,
            e.username,
            e.age,
            e.sex,

            d.id,
            d.name
        from employee e join dept d on d.id = e.dept_id
    </select>

 

但是这个SQL也查询不到关联对象 ,ResultType只能根据查询的列把对应的值封装到实体类的属性中,Employee中的Dept是一个自定义的字段,如果查询的列明和对象中的属性名不一致,就需要用到resultMap。如下:  

<resultMap id="baseResultMap" type="cn.lzh.mybatis._03_many_2_one.domain.Employee">
        <id column="e_id" property="id" />
        <result column="age" property="age" />
        <result column="username" property="username" />
        <result column="sex" property="sex" />
        <result column="age" property="age" />
        <!-- 处理关联对象的映射
            property="dept"  :对employee.dept属性的映射
            javaType="..Dept" :  employee.dept的类型 -->
        <association property="dept" javaType="cn.lzh.mybatis._03_many_2_one.domain.Dept">
            <id column="d_id" property="id" />
            <result column="name" property="name" />
        </association>
</resultMap>
<select id="selectAll" resultMap="baseResultMap">
    select
    e.id as e_id,
    e.username,
    e.age,
    e.sex,

    d.id as d_id,
    d.name
    from employee e join dept d on d.id = e.dept_id
</select>

 


4多对一查询-嵌套查询

上面的方式是第一种查询方式,我们叫着嵌套结果,意思就是查询的SQL使用JSON连表的方式把要查询的内容employee和dept全部查询出来 ,然后使用ResultMap来处理结果。

还有一种方式叫嵌套查询,这种方式在查询SQL的时候,只需要查询employee表即可,不需要去连表查询Dept,而是在ResultMap中额外发一个子SQL去查询emloyee关联的dept的数据,然后映射给Employee。

总结一下区别:前者是在SQL连表查询出两个表的内容,然后在ResultMap处理关系,映射结果。而后者是在SQL查询不连表,而是在ResultMap额外发SQL查询管理的对象。

1.修改xml,使用嵌套查询

<resultMap id="baseResultMap2" type="cn.lzh.mybatis._03_many_2_one.domain.Employee">
        <id column="e_id" property="id" />
        <result column="age" property="age" />
        <result column="username" property="username" />
        <result column="sex" property="sex" />
        <result column="age" property="age" />
        <association property="dept"
                     javaType="cn.lzh.mybatis._03_many_2_one.domain.Dept"
                     column="dept_id"                  select="cn.lzh.mybatis._03_many_2_one.mapper.DeptMapper.selectById"
                     
        />
    </resultMap>

    <select id="selectAll2" resultMap="baseResultMap">
        select
            e.id as e_id,
            e.username,
            e.age,
            e.sex,
            e.dept_id
        from employee
    </select>

 

 

这里我们的SQL并没有去关联dept,但是在resultMap处理结果集的时候,使用了<association 来映射 dept,

  • select="....DeptMapper.selectById" : 这里的意思是额外发一条SQL去查询当前employee关联的dept

  • column="dept_id" :额外SQL的参数使用 employee表中的dept_id,

在deptMapper.xml编写查询的SQL

 

<mapper namespace="cn.lzh.mybatis._03_many_2_one.mapper.DeptMapper">

    <select id="selectById" resultType="cn.lzh.mybatis._03_many_2_one.domain.Dept">
        select id,name from dept where id = #{id}
    </select>
    //....

5懒加载配置

 开启懒加载配置

<settings>
    <!--延迟加载总开关  true 开启延迟加载  false关闭延迟加载,即关联查询的时候会立刻查询关联对象-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--侵入式延迟加载开关:
    侵入式延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询
    默认是false
    查询主加载对象的任何属性时,都要执行关联对象的查询-->
    <setting name="aggressiveLazyLoading" value="false"/>
    <!-- lazyLoadTriggerMethods:指定对象的方法触发一次延迟加载。默认值:equals() clone() hashCode() ) toString() -->
    <setting name="lazyLoadTriggerMethods" value=""/>
</settings>

 

三.Sql编写高级特性-一对多

1.一对多保存

1.建立对象之间关系

//一对多,一方
public class ProductType {
    private Long id;
    private String name;

    //维护关系
    private List<Product> productList = new ArrayList<>();
----------------------------------------------------------------
//一对多,多
public class Product {
    private Long id;
    private String productName;
    private Long productTypeId;

 

 

并且建立表的关系

2.编写ProductTypeMapper接口,insert方法

3.编写ProductMapper接口,insert方法

4.编写ProductTypeMapper.xml,insert方法

<?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">

<!--
    namespace:命名空间 ,sql坐标(SQL在哪儿)
-->
<mapper namespace="cn.lzh.mapper.ProductTypeMapper">
    <insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        INSERT INTO product_type(name) values (#{name})
    </insert>
</mapper>

 

5.编写ProductMapper.xml,insert方法  

<?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">

<!--
    namespace:命名空间 ,sql坐标(SQL在哪儿)
-->
<mapper namespace="cn.lzh.mapper.ProductMapper">


    <insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        INSERT INTO product(product_name,product_type_id)

        values (#{productName},#{productTypeId})
    </insert>
</mapper>

 

6.编写Dao  

public class ProductDaoImpl implements IProductDao {
    @Override
    public void insert(Product product) {
        try(SqlSession sqlSession = MyBatisUtil.openSession()){
            ProductMapper mapper = sqlSession.getMapper(ProductMapper.class);
            mapper.insert(product);
            sqlSession.commit();
        }
    } 

7.编写测试  

public class One2ManyTest {

    private IProductTypeDao productTypeDao = new ProductTypeDaoImpl();
    private IProductDao productDao = new ProductDaoImpl();

    @Test
    public void testAddProductType(){

        ProductType productType = new ProductType();
        productType.setName("鼠标");
        productTypeDao.insert(productType);

        productType.setProductList(Arrays.asList(
                new Product("鼠标2",productType.getId()),
                new Product("鼠标3",productType.getId()),
                new Product("鼠标4",productType.getId())
        ));

        productType.getProductList().forEach( product -> {
            productDao.insert(product);
        });
    }

}
 

 

四.缓存的使用

 

 

1.一级缓存

 一级缓存默认开启,在SqlSession中,要命中一级缓存需要使用同一个SqlSession发送相同的SQL。

 //通过ID查询
    @Override
    public Product selectById(Long id) throws IOException {
        try(
            //4.得到SqlSession : sql回话对象,用来执行Sql语句,包括事务的提交,回滚等
            SqlSession sqlSession = MyBatisUtil.openSession();
        ){

            ProductMapper mapper = sqlSession.getMapper(ProductMapper.class);
            //一级缓存: 相同的SqlSession,相同的SQL
            //第一次查询 ,走MySql 把结果 以    sql=对象  的方式缓存到SqlSession
            //第二次查询,还是这个SqlSesion,还是这个SQL,那么就会命中缓存 , 直接返回值
            mapper.selectById(id);
            mapper.selectById(id);
        }
        return null;
    }

2.二级缓存

二级缓存在SqlSessionFactory中,要命中的条件是同一个SqlSessionFactory,发送相同的SQL。缓存是以namespace为单位的,不同namespace下的操作互不影响。

mapper.xml增加:

<cache />  开启二级缓存 

mybatis-config.xml :开启二级缓存  

<settings>
      <setting name="cacheEnabled" value="true"></setting>
</settings> 

 实体类实现序列化接口

class Employee implement Serilizable{
...

测试二级缓存 ,测试的时候,sqlSession执行完之后要提交事务,才能看到二级缓存效果。

1 一级是Sqlsession级别,一般我们不会直接使用,框架为了关联查询提高效率

2 二级缓存是SqlSessionFactory(整个应用中只有一份),可以使用来增强效率。

 

 

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值