GVKun编程网logo

Mybatis的缓存与动态SQL(mybatis怎么保持缓存和数据库一致性)

11

如果您对Mybatis的缓存与动态SQL和mybatis怎么保持缓存和数据库一致性感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解Mybatis的缓存与动态SQL的各种细节,并对mybatis怎

如果您对Mybatis的缓存与动态SQLmybatis怎么保持缓存和数据库一致性感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解Mybatis的缓存与动态SQL的各种细节,并对mybatis怎么保持缓存和数据库一致性进行深入的分析,此外还有关于day64(MYBATIS框架基础2:查询数据,动态SQL ,关联查询,小结,关于#{}和${}格式的占位符,Mybatis的缓存机制)、java web(七): mybatis的动态sql和mybatis generator自动生成pojo类和映射文件、JavaEE_Mybatis_SpringMVC__Mybatis_lesson8_Mybatis的动态sql、MyBatis - 常用标签与动态Sql的实用技巧。

本文目录一览:

Mybatis的缓存与动态SQL(mybatis怎么保持缓存和数据库一致性)

Mybatis的缓存与动态SQL(mybatis怎么保持缓存和数据库一致性)

Mybatis的缓存

缓存也是为了减少java应用与数据库的交互次数,提升程序的效率

一级缓存

自带一级缓存,并且无法关闭,一直存在,存储在sqlSession中

使用同一个sqlsession进行查询操作一级缓存存在;如果有多个sqlsession那么一级缓存不存在

缓存一般争对查询,如果进行了增删改查操作,会自动的将缓存的数据清除,保证数据的一致性,一级缓存不需要设置,直接使用即可。

1.实体类

package com.shouthwind.entity;

import lombok.Data;

import java.util.List;

@Data
public class Account {
    private Integer id;
    private String name;
    private List<Course> courses;
}

2.接口

package com.shouthwind.repository;

import com.shouthwind.entity.Account;

public interface AccountRepository {
    public Account findById1(Integer id);
    public Account findById(Integer id);
}

3.Mapper

<?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.shouthwind.repository.AccountRepository">
    <resultMap id="accoutMap" type="com.shouthwind.entity.Account">
        <id column="aid" property="id"></id>
        <result column="aname" property="name"></result>
        <collection property="courses" ofType="com.shouthwind.entity.Course">
            <id column="cid" property="id"></id>
            <result column="cname" property="name"></result>
        </collection>
    </resultMap>
 <select id="findById1" parameterType="java.lang.Integer" resultMap="accoutMap">
     select a.id aid,a.name aname,c.id cid,c.name cname from account a,t_course c,account_course ac where a.id=#{id} and a.id=ac.aid and c.id=ac.cid
 </select>
    <select id="findById" parameterType="java.lang.Integer" resultType="com.shouthwind.entity.Account">
        select * from  account where id=#{id}
    </select>
</mapper>

4.测试

对一个sql session一直使用查询的结果

二级缓存

它是Mapper级别的,无论使用多少个sqlsession都可以共享。

二级缓存默认是关闭的,二级缓存配置开启。

  • 自带的二级缓存
  • 第三方的ehcache二级缓存

自带的二级缓存

1.配置config.xml

<!--        打印sql-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
<!--        延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
<!--        开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

2.Mapper.xml配置二级缓存

<?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.shouthwind.repository.StudentRepository">

    <cache></cache>

    <resultMap id="studentMap" type="com.shouthwind.entity.Student">
        <id column="sSno" property="Sno"></id>
        <result column="sSname" property="Sname"></result>
        <result column="sSsex" property="Ssex"></result>
        <result column="sSage" property="Sage"></result>
        <result column="sSdept" property="Sdept"></result>
        <association property="sc"  javaType="com.shouthwind.entity.Sc">
            <result column="cSno" property= "Sno"></result>
            <result column="cCno" property="Cno"></result>
            <result column="cGrade" property="Grade"></result>
        </association>
    </resultMap>
    <select id="findById" parameterType="java.lang.Integer" resultMap="studentMap">
        select s.Sno sSno,s.Sname sSname,s.Ssex sSsex,s.Sage sSage,s.Sdept sSdept,c.Sno cSno,c.Cno cCno,c.Grade cGrade from student s,sc c where s.Sno=c.Sno and s.Sno=#{Sno}
    </select>
</mapper>

3.实体类实现Serializable接口

package com.shouthwind.entity;

import lombok.Data;

import java.io.Serializable;

@Data
public class Student implements Serializable {
    private  Integer Sno;
    private String Sname;
    private String Ssex;
    private Integer Sage;
    private String Sdept;
    private  Sc sc;
}

第三方的ehcache缓存

1.配置

<!--        ehcache-->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>2.4.3</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.0.0</version>
        </dependency>

2.在resource小创建ehcache创建配置

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
    <!-- 磁盘保存路径 -->
    <diskStore path="D:\\ehcache" />

    <defaultCache
            maxElementsInMemory="10000"
            maxElementsOndisk="10000000"
            eternal="false"
            overflowTodisk="true"
            timetoIdleSeconds="120"
            timetoLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreevictionPolicy="LRU">
    </defaultCache>
</ehcache>

3.config.xml中配置二级缓存

    <settings>
<!--        打印sql-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
<!--        延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
<!--        开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

4.Mapper.xml中配置二级缓存

    <cache type="org.mybatis.caches.ehcache.EhcacheCache">
<!--        缓存创建以后,最后一次访问的时间到失效时间-->
        <property name="timetoIdleSeconds" value="3600"/>
<!--        缓存自创建时间起自失效时间的间隔-->
        <property name="timetoLiveSeconds" value="3600"/>
<!--        缓存回收策略,LRU移除近期的最少使用的对象-->
        <property name="memoryStoreevictionPolicy" value="LRU"/>
    </cache>

5.实体类不需要实现序列化接口

MysqL的动态sql

Mybatis的动态sql可以更据不同的信息来拼接不同的sql、以适应不同的需求

1.创建实体类

package com.shouthwind.entity;

import lombok.Data;

@Data
public class People {
//    private Integer Sno;
//    private String Sname;
//    private String Ssex;
//    private Integer Sage;
//    private String Sdept;
    private Integer id;
    private String name;
    private Integer money;
}

2.创建Mapper接口和文件

package com.shouthwind.repository;

import com.shouthwind.entity.People;

import java.util.List;

public interface UserRepository {
//    public int save(People people);
//    public int deleteById(Integer id);
//    public  int update(People people);
//    public People findById(Integer id);
//    public List<People> findAll();
//    public  People findById2(int id);
//    public  People findByName(String name);
//    public  People findByIdAndName(Integer id,String name);
//    public Integer count();
//    public  String findNameById(Integer id);
    public People findByUser (People people);
}

<!--    动态  sql-->
    <select id="findByUser" parameterType="com.shouthwind.entity.People" resultType="com.shouthwind.entity.People">
        select  * from  people
        <where>
            <if test="id!=null">
                id=#{id}
            </if>
            <if test="name!=null">
                and name={name}
            </if>
            <if test="money!=null">
                and money=#{money}
            </if>
        </where>
    </select>

4.测试

package com.shouthwind.Test;

import com.shouthwind.entity.People;
import com.shouthwind.repository.UserRepository;
import org.apache.ibatis.session.sqlSession;
import org.apache.ibatis.session.sqlSessionFactory;
import org.apache.ibatis.session.sqlSessionFactoryBuilder;

import java.io.InputStream;

public class Test7 {
    public static void main(String[] args) {
        InputStream inputStream = Test7.class.getClassLoader().getResourceAsstream("config.xml");
        sqlSessionFactoryBuilder sqlSessionFactoryBuilder = new sqlSessionFactoryBuilder();
        sqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        sqlSession sqlSession = sqlSessionFactory.openSession();
        //获取实现了自定义代理接口的对象
        UserRepository userRepository=sqlSession.getMapper(UserRepository.class);
        People people = new People();
        people.setId(1);
        People result =userRepository.findByUser(people);
        System.out.println(result);
    }
}

if

where

  • choose、when
    类似swich case

代码:

<!--    动态  sql-->
    <select id="findByUser" parameterType="com.shouthwind.entity.People" resultType="com.shouthwind.entity.People">
        select  * from  people
        <where>
        <choose>
            <when test="id!=null">
                id=#{id}
            </when>
            <when test="name!=null">
                and name={name}
            </when>
            <when test="money!=null">
                and money=#{money}
            </when>
        </choose>
        </where>
    </select>
  • trim
    设置predix和suffix参数来完成使用

  • set用于update操作
    package com.shouthwind.repository;

    import com.shouthwind.entity.People;
    
    import java.util.List;
    
    public interface UserRepository {
    //    public int save(People people);
    //    public int deleteById(Integer id);
    //    public  int update(People people);
    //    public People findById(Integer id);
    //    public List<People> findAll();
    //    public  People findById2(int id);
    //    public  People findByName(String name);
    //    public  People findByIdAndName(Integer id,String name);
    //    public Integer count();
    //    public  String findNameById(Integer id);
        public People findByUser (People people);
        public Integer update (People people);
    }
    

    mapperi文件

    update people


    name=#{name},


    money=#{money}


    where id=#{id}

测试类:

package com.shouthwind.Test;

import com.shouthwind.entity.People;
import com.shouthwind.repository.UserRepository;
import org.apache.ibatis.session.sqlSession;
import org.apache.ibatis.session.sqlSessionFactory;
import org.apache.ibatis.session.sqlSessionFactoryBuilder;

import java.io.InputStream;

public class Test7 {
    public static void main(String[] args) {
        InputStream inputStream = Test7.class.getClassLoader().getResourceAsstream("config.xml");
        sqlSessionFactoryBuilder sqlSessionFactoryBuilder = new sqlSessionFactoryBuilder();
        sqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        sqlSession sqlSession = sqlSessionFactory.openSession();
        //获取实现了自定义代理接口的对象
        UserRepository userRepository=sqlSession.getMapper(UserRepository.class);
        People people = new People();
        people.setId(1);
        people.setName("郝momo");
        people.setMoney(55);
        userRepository.update(people);
        sqlSession.commit();
//        People result =userRepository.findByUser(people);
//        System.out.println(result);
        sqlSession.close();
    }
}

day64(MYBATIS框架基础2:查询数据,动态SQL ,关联查询,小结,关于#{}和${}格式的占位符,Mybatis的缓存机制)

day64(MYBATIS框架基础2:查询数据,动态SQL ,关联查询,小结,关于#{}和${}格式的占位符,Mybatis的缓存机制)

day64(MYBATIS框架基础2:查询数据,动态sql ,关联查询,小结,关于#{}和${}格式的占位符,Mybatis的缓存机制)

1.查询数据

1.统计

  • 查询当前表有多少条数据,sql语句为

  • select count(*) from ams_admin
  • 在AdminMapper接口中添加抽象方法

    int count();
  • 在AdminMapper.xml中配置以上抽象方法映射的sql语句

    <!-- int count(); -->
    <select id="count" resultType="int">
    select count(*) from ams_admin
    </select>

    注意:所有select节点必须配置resultType或resultMap这2个属性中的其中1个

  • 在MyDetailsTest.java编写并执行测试

    @Test
    public void testCount() {
    AnnotationConfigApplicationContext ac
    = new AnnotationConfigApplicationContext(SpringConfig.class);
    AdminMapper adminMapper = ac.getBean(AdminMapper.class);
    int count = adminMapper.count();
    System.out.println("当前表中有" + count + "条记录");
    ac.close();
    }
  • 2.查询某一条数据

    1.普通查询

  • 目标:根据id查询管理员信息 ,sql语句

    select * from ams_admin where id=?
  • 在AdminMapper接口中添加抽象方法

    Admin getById(Long id);
  • 在AdminMapper.xml中配置以上抽象方法映射的sql语句

    <!-- Admin getById(Long id); -->
    <select id="getById" resultType="cn.tedu.mybatis.Admin">
    select * from ams_admin where id=#{id}
    </select>
  • 在MyDetailsTest.java编写并执行测试:

    @Test
    public void testGetById() {
    AnnotationConfigApplicationContext ac
    = new AnnotationConfigApplicationContext(SpringConfig.class);
    AdminMapper adminMapper = ac.getBean(AdminMapper.class);
    Long id = 3L;
    Admin admin = adminMapper.getById(id);
    System.out.println("查询结果:" + admin);
    ac.close();
    }
  • 通过测试可以发现:当存在匹配的数据时,将可以查询到数据,当不存在匹配的数据时,将返回null

  • 2.封装查询(resultMap)

  • 需要注意,如果查询结果集中的列名与类的属性名不匹配时,默认将放弃处理这些结果数据,则返回的对象中对应的属性值为null

  • 为了解决此问题,可以在查询时使用自定义的别名,使得名称保持一致,不过,更推荐配置<resultMap>以指导Mybatis封装查询结果

  • resultMap属性

  • resultMap节点的作用是:指导Mybatis如何将结果集中的数据封装到返回的对象中

  • id属性:自定义名称

  • type属性:将结果集封装到哪种类型的对象中

  • column属性:列名

  • property属性:属性名

  • 例子:

  • ResultMap使用示例

    <select id="getById" resultMap="BaseResultMap">
    select * from ams_admin where id=#{id}
    </select>
    <!-- resultMap节点的作用是:指导Mybatis如何将结果集中的数据封装到返回的对象中 -->
    <!-- id属性:自定义名称 -->
    <!-- type属性:将结果集封装到哪种类型的对象中 -->
    <resultMap id="BaseResultMap" type="cn.tedu.mybatis.Admin">
    <!-- 使用若干个result节点配置名称不统一的对应关系 -->
    <!-- 在单表查询中,名称本来就一致的是不需要配置的 -->
    <!-- column属性:列名 -->
    <!-- property属性:属性名 -->
    <result column="is_enable" property="isEnable" />
    <result column="last_login_ip" property="lastLoginIp" />
    <result column="login_count" property="loginCount" />
    <result column="gmt_last_login" property="gmtLastLogin" />
    <result column="gmt_create" property="gmtCreate" />
    <result column="gmt_modified" property="gmtModified" />
    </resultMap>
  • 3.查询列表

  • 查询所有管理员信息sql

    select * from ams_admin order by id
  • 在AdminMapper接口中添加抽象方法

    List<Admin> list();
  • 在AdminMapper.xml中配置以上抽象方法映射的sql语句

    <!-- List<Admin> list(); -->
    <select id="list" resultMap="BaseResultMap">
    select * from ams_admin order by id
    </select>

    注意:(1) 查询时,结果集中可能超过1条数据时,必须显式的使用ORDER BY子句对结果集进行排序;(2) 查询时,结果集中可能超过1条数据时,应该考虑是否需要分页

  • 在MyDetailsTest.java编写并执行测试:

    @Test
    public void testList() {
    AnnotationConfigApplicationContext ac
    = new AnnotationConfigApplicationContext(SpringConfig.class);
    AdminMapper adminMapper = ac.getBean(AdminMapper.class);
    List<Admin> list = adminMapper.list();
    for (Admin admin : list) {
    System.out.println(admin);
    }
    ac.close();
    }
  • 2.动态sql

    1.概念

    根据参数不同,生成不同的sql语句

  • 举例

    你可以对某些参数值进行判断,根据判断结果走向不同分支,来决定sql语句的某个片段,如果参数值是可遍历的,你还可以遍历此参数来生成部分sql片段

  • 2.实现

  • 目标: 根据若干个id一次性删除若干条管理数据 sql

    delete from ams_admin where id in (?,?)
  • 注意:以上sql语句中,id值的数量(以上?的数量)对于开发人员而言是未知的,通常是软件的使用者决定的

  • 在AdminMapper接口中添加抽象方法:

    int deleteByIds(Long... ids);
  • int deleteByIds(Long[] ids);
  • int deleteByIds(List<Long> ids);
    
  • 在AdminMapper.xml中配置以上抽象方法映射的sql语句:

    <!-- int deleteByIds(List<Long> ids); -->
    <delete id="deleteByIds">
    delete from ams_admin where id in (
    <foreach collection="list" item="id" separator="
    ,
    ">
    #{id}
    </foreach>
    )
    </delete>
    
  • 以上代码中:

  • foreach标签:用于遍历集合或数组类型的参数对象

  • collection属性:被遍历的参数对象,当抽象方法的参数只有1个且没有添加@Param注解时,如果参数是List类型则此属性值为list,如果参数是数组类型(包括可变参数)则此属性值为array;当抽象方法的参数有多个或添加了@Param注解时,则此属性值为@Param注解中配置的值

  • item属性:自定义的名称,表示遍历过程中每个元素的变量名,可在foreach子级使用#{变量名}表示数据

  • separator属性:分隔符号,会自动添加在遍历到的各元素之间

  • 在MyDetailsTest.java编写并执行测试

    @Test
    public void testDeleteByIds() {
    AnnotationConfigApplicationContext ac
    = new AnnotationConfigApplicationContext(SpringConfig.class);
    AdminMapper adminMapper = ac.getBean(AdminMapper.class);
    List<Long> ids = new ArrayList<>();
    ids.add(16L);
    ids.add(18L);
    ids.add(19L);
    int rows = adminMapper.deleteByIds(ids);
    System.out.println("受影响的行数为:" + rows);
    ac.close();
    }
    
  • 3.其它

  • 在Mybatis中动态sql还有其它节点,例如:

  • if

  • choose/when/otherwise

  • 3.关联查询

    1.前期准备

  • 准备一些数据表,及必要的测试数据,例如:存在若干条用户数据,存在若干条角色数据,某个用户存在与角色的关联,最好有些用户有多个关联,又有些用户只有1个关联,还有些用户没有关联

  • 2.RBAC

    1.概念

  • RBAC = Role Based Access Control(基于角色的访问控制)

  • RBAC是经典的用户权限管理的设计思路。在这样的设计中,会存在3种类型:用户、角色、权限,权限将分配到各种角色上,用户可以关联某种角色,进而实现用户与权限相关。使用这样的设计,更加利于统一管理若干个用户的权限。

  • 在RBAC的设计思路中,用户与角色一般是多对多的关系,而在数据库中,仅仅只是使用“用户”和“角色”这2张表是不利于维护多对多关系的,通常会增加一张中间表,专门记录对应关系,同理,角色和权限也是多对多的关系,也需要使用中间表来记录对应关系!

  • 2.举例: 典型的RBAC设计

    1.准备工作(在console中添加)
  • ams_admin:管理员表

    -- 管理员表:创建数据表
    drop table if exists ams_admin;
    create table ams_admin (
    id bigint unsigned auto_increment,
    username varchar(50) default null unique comment '用户名',
    password char(64) default null comment '密码(密文)',
    nickname varchar(50) default null comment '昵称',
    avatar varchar(255) default null comment '头像URL',
    phone varchar(50) default null unique comment '手机号码',
    email varchar(50) default null unique comment '电子邮箱',
    description varchar(255) default null comment '描述',
    is_enable tinyint unsigned default 0 comment '是否启用,1=启用,0=未启用',
    last_login_ip varchar(50) default null comment '最后登录IP地址(冗余)',
    login_count int unsigned default 0 comment '累计登录次数(冗余)',
    gmt_last_login datetime default null comment '最后登录时间(冗余)',
    gmt_create datetime default null comment '数据创建时间',
    gmt_modified datetime default null comment '数据最后修改时间',
    primary key (id)
    ) comment '管理员' charset utf8mb4;
    
  • ams_role:角色表

    -- 角色表:创建数据表
    drop table if exists ams_role;
    create table ams_role (
    id bigint unsigned auto_increment,
    name varchar(50) default null comment '名称',
    description varchar(255) default null comment '描述',
    sort tinyint unsigned default 0 comment '自定义排序序号',
    gmt_create datetime default null comment '数据创建时间',
    gmt_modified datetime default null comment '数据最后修改时间',
    primary key (id)
    ) comment '角色' charset utf8mb4;
    
  • ams_admin_role:管理员与角色的关联表

    -- 管理员角色关联表:创建数据表
    drop table if exists ams_admin_role;
    create table ams_admin_role (
    id bigint unsigned auto_increment,
    admin_id bigint unsigned default null comment '管理员id',
    role_id bigint unsigned default null comment '角色id',
    gmt_create datetime default null comment '数据创建时间',
    gmt_modified datetime default null comment '数据最后修改时间',
    primary key (id)
    ) comment '管理员角色关联' charset utf8mb4;
    
  • ams_permission:权限表

    -- 权限表:创建数据表
    drop table if exists ams_permission;
    create table ams_permission (
    id bigint unsigned auto_increment,
    name varchar(50) default null comment '名称',
    value varchar(255) default null comment '值',
    description varchar(255) default null comment '描述',
    sort tinyint unsigned default 0 comment '自定义排序序号',
    gmt_create datetime default null comment '数据创建时间',
    gmt_modified datetime default null comment '数据最后修改时间',
    primary key (id)
    ) comment '权限' charset utf8mb4;
    
  • ams_role_permission:角色与权限的关联表

    -- 角色权限关联表:创建数据表
    drop table if exists ams_role_permission;
    create table ams_role_permission (
    id bigint unsigned auto_increment,
    role_id bigint unsigned default null comment '角色id',
    permission_id bigint unsigned default null comment '权限id',
    gmt_create datetime default null comment '数据创建时间',
    gmt_modified datetime default null comment '数据最后修改时间',
    primary key (id)
    ) comment '角色权限关联' charset utf8mb4;
    
  • 插入测试数据

    truncate ams_admin;
    truncate ams_admin_role;
    truncate ams_role;
    truncate ams_permission;
    insert into ams_admin (username, password) values ('admin001', '123456');
    insert into ams_admin (username, password) values ('admin002', '123456');
    insert into ams_admin (username, password) values ('admin003', '123456');
    insert into ams_admin (username, password) values ('admin004', '123456');
    insert into ams_admin (username, password) values ('admin005', '123456');
    
    insert into ams_permission (name, value, description) values
    ('商品-商品管理-读取', '/pms/product/read', '读取商品数据,含列表、详情、查询等'),
    ('商品-商品管理-编辑', '/pms/product/update', '修改商品数据'),
    ('商品-商品管理-删除', '/pms/product/delete', '删除商品数据'),
    ('后台管理-管理员-读取', '/ams/admin/read', '读取管理员数据,含列表、详情、查询等'),
    ('后台管理-管理员-编辑', '/ams/admin/update', '编辑管理员数据'),
    ('后台管理-管理员-删除', '/ams/admin/delete', '删除管理员数据');
    insert into ams_role (name) values
    ('超级管理员'), ('系统管理员'), ('商品管理员'), ('订单管理员');
    insert into ams_admin_role (admin_id, role_id) values
    (1, 1), (1, 2), (1, 3), (1, 4),
    (2, 1), (2, 2), (2, 3),
    (3, 1), (3, 2),
    (4, 1);
    
  • 2.练习
  • 当有了新的数据表后,你应该在课后使用这些表继续练习常规数据管理,例如:

  • 向权限表中插入新的数据

  • 根据id删除某个权限

  • 查询权限表中的所有权限

  • 提示:

  • 需要新的数据类型,例如Permission类

  • 需要新的接口文件,用于定义以上2个数据访问功能的抽象方法

  • 需要新的XML文件,用于配置抽象方法对应的sql语句

  • 需要修改配置信息,将此前指定的XML文件由AdminMapper.xml改为*.xml,并把SpringConfig类中sqlSessionfactorybean()方法的第2个参数由Resource类型改为Resource...类型

  • 当需要测试时,使用新的测试类

  • 3.关联查询

  • sql语句

    select *
    from ams_admin
    left join ams_admin_role on ams_admin.id=ams_admin_role.admin_idleft join ams_role on ams_admin_role.role_id=ams_role.id
    where ams_admin.id=?
    
  • 注意:在《Java开发手册》中在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。

  • 由于不建议使用星号表示字段列表,而关联查询时,由于涉及多张表,则字段列表可能较长,可以使用节点封装字段列表,并在编写sql语句时,使用节点include进行引用

  • 提示:在IntelliJ IDEA中,如果在sql节点中直接写字段列表,会提示错误,这是IntelliJ IDEA的误判,可以无视,并不影响运行,或者,使用其它方式避免误判,例如添加if恒等式(见下页示例)

  • 注意:使用节点可以封装sql语句的任何部分,而封装字段列表是最常见的做法

  • 使用与的简单示例:

    <sql id="SimpleQueryFields">
    <if test="true">
    id,
    username,
    password
    </if>
    </sql>
    <select id="getById" resultType="xx.xx.xx.AdminVO">
    select
    <include refid="SimpleQueryFields" />
    from ams_admin
    where id=#{id}
    </select>
    
  • 如果使用封装了查询的字段列表,与的相性更好,所以,在开发实践中,通常结合一起使用

  • 另外,在开发实践中,许多不同条件的查询的字段列表是相同的,使用可以很好的实现代码片段的复用

  • 通过测试运行,可以发现(必须基于以上测试数据):

  • 当使用的id值为1时,共查询到4条记录,并且用户的基本信息是相同的,只是与角色关联的数据不同

  • 当使用的id值为2时,共查询到3条记录

  • 当使用的id值为3时,共查询到2条记录

  • 当使用其它有效用户的id时,共查询到1条记录

  • 此类查询期望的结果应该是:

    public class xxx {
    // 用户基本信息的若干个属性,例如用户名、密码等
    // 此用户的若干个角色数据,可以使用 List<?>
    }
    
  • 先创建“角色”对应的数据类型:

    public class Role {
    private Long id;
    private String name;
    private String description;
    private Integer sort;
    private LocalDateTime gmtCreate;
    private LocalDateTime gmtModified;
    // Setters & GetteRSS
    // toString()
    }
    
  • 创建用于封装此次查询结果的类型 :

    public class AdminDetailsVO { 
        private Long id; 
        private String username; 
        private String password; 
        private String nickname; 
        private String avatar; 
        private String phone; 
        private String email; 
        private String description; 
        private Integer isEnable; 
        private String lastLoginIp; 
        private Integer loginCount; 
        private LocalDateTime gmtLastLogin; 
        private LocalDateTime gmtCreate; 
        private LocalDateTime gmtModified; 
        private List<Role> roles; 
        // Setters & GetteRSS // toString()
    }
    
  • 在AdminMapper接口中添加抽象方法:

    AdminDetailsVO getDetailsById(Long id);
    
  • 需要注意,由于此次关联了3张表一起查询,结果集中必然出现某些列的名称是完全相同的,所以,在查询时,不可以使用星号表示字段列表(因为这样的结果集中的列名就是字段名,会出现相同的列名),而是应该至少为其中的一部分相同名称的列定义别名

  • 举例

    select
    ams_admin.id, ams_admin.username, ams_admin.password, ams_admin.nickname,ams_admin.avatar, ams_admin.phone, ams_admin.email, ams_admin.description,ams_admin.is_enable, ams_admin.last_login_ip, ams_admin.login_count,
    ams_admin.gmt_last_login, ams_admin.gmt_create, ams_admin.gmt_modified,
    ams_role.id AS role_id,
    ams_role.name AS role_name,
    ams_role.description AS role_description,
    ams_role.sort AS role_sort,
    ams_role.gmt_create AS role_gmt_create,
    ams_role.gmt_modified AS role_gmt_modified
    from ams_admin
    left join ams_admin_role on ams_admin.id=ams_admin_role.admin_id
    left join ams_role on ams_admin_role.role_id=ams_role.id
    where ams_admin.id=1;
    
  • 在Mybatis处理中此查询时,并不会那么智能的完成结果集的封装,所以,必须自行配置resultMap用于指导Mybatis完成封装!

    <resultMap id="DetailsResultMap" type="xx.xx.xx.xx.AdminDetailsVO">
    <!-- 在1对多、多对多的查询中,即使名称匹配的结果,也必须显式的配置 -->
    <!-- 主键字段的结果必须使用id节点进行配置,配置方式与result节点完全相同 -->
    <id column="id" property="id" />
    <result column="gmt_create" property="gmtCreate" />
    <!-- 需要使用collection节点配置1对多中“多”的数据 -->
    <collection property="roles" ofType="xx.xx.xx.Role">
    <id column="role_id" property="id" />
    <result column="gmt_create" property="gmtCreate" />
    </collection>
    </resultMap>
    
  • 完整的XML示例:

    <?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属性:必须的,用于指定此XML文件对应的接口,取值为接口的全限定名 -->
    <mapper namespace="cn.tedu.mybatis.mapper.AdminMapper">
    
        <!-- 根据要执行的数据操作的类型来选择insert/delete/update/select节点 -->
        <!-- 节点的id是抽象方法的名称(仅名称) -->
        <!-- 节点的内部编写sql语句 -->
        <!-- sql语句中的参数值使用#{}格式的占位符 -->
        <insert id="insert" useGeneratedKeys="true" keyProperty="id">
            insert into ams_admin (
                username, password, nickname, avatar,
                phone, email, description, is_enable,
                last_login_ip, login_count, gmt_last_login,
                gmt_create, gmt_modified
            ) values (
                         #{username}, #{password}, #{nickname}, #{avatar},
                         #{phone}, #{email}, #{description}, #{isEnable},
                         #{lastLoginIp}, #{loginCount}, #{gmtLastLogin},
                         #{gmtCreate}, #{gmtModified}
                     )
        </insert>
    
        <insert id="insertBatch">
            insert into ams_admin (username) values
            <foreach collection="list" item="username" separator=",">
                (#{username})
            </foreach>
        </insert>
    
        <delete id="deleteById">
            delete from ams_admin where id=#{id}
        </delete>
    
        <!-- foreach节点是用于对参数值进行遍历的 -->
        <!-- collection属性:被遍历对象 -->
        <!-- 如果抽象方法的参数只有1个,当参数是数组时,collection="array",当参数是List时,collection="list" -->
        <!-- 如果抽象方法的参数只有多个,则collection="参数名",例如通过@Param注解配置的名称 -->
        <!-- item属性:自定义名称,是被遍历的对象的名称 -->
        <!-- separator属性:遍历过程中各值之间的分隔符号 -->
        <delete id="deleteByIds">
            delete from ams_admin where id in (
            <foreach collection="array" item="id" separator=",">
                #{id}
            </foreach>
            )
        </delete>
    
        <update id="updatePasswordById">
            update ams_admin set password=#{password} where id=#{id}
        </update>
    
        <!-- 查询所使用的必须是select节点 -->
        <!-- select节点必须配置resultType或resultMap中的其中1个 -->
        <!-- resultType的值就是抽象方法的返回值类型的全限定名 -->
        <select id="count" resultType="int">
            select count(*) from ams_admin
        </select>
    
        <select id="getById" resultMap="BaseResultMap">
            select
            <include refid="xxx"/>
            from ams_admin where id=#{id}
        </select>
    
        <select id="list" resultMap="BaseResultMap">
            select
            <include refid="xxx"/>
            from ams_admin order by id
        </select>
    
        <sql id="xxx">
            <if test="true">
                id, username, password
            </if>
        </sql>
    
        <!-- AdminDetailsVO getDetailsById(Long id); -->
        <select id="getDetailsById" resultMap="DetailsResultMap">
            select
            <include refid="DetailsQueryFields"/>
            from ams_admin
            left join ams_admin_role on ams_admin.id = ams_admin_role.admin_id
            left join ams_role on ams_role.id = ams_admin_role.role_id
            where ams_admin.id=#{id}
        </select>
    
        <sql id="DetailsQueryFields">
            <if test="true">
                ams_admin.id,
                ams_admin.username,
                ams_admin.password,
                ams_admin.nickname,
                ams_admin.avatar,
                ams_admin.phone,
                ams_admin.email,
                ams_admin.description,
                ams_admin.is_enable,
                ams_admin.last_login_ip,
                ams_admin.login_count,
                ams_admin.gmt_last_login,
                ams_admin.gmt_create,
                ams_admin.gmt_modified,
    
                ams_role.id AS role_id,
                ams_role.name AS role_name,
                ams_role.description AS role_description,
                ams_role.sort AS role_sort,
                ams_role.gmt_create AS role_gmt_create,
                ams_role.gmt_modified AS role_gmt_create
            </if>
        </sql>
    
        <!-- resultMap节点的作用:指导mybatis将查询到的结果集封装到对象中 -->
        <!-- resultMap节点的id属性:自定义名称 -->
        <!-- resultMap节点的type属性:封装查询结果的类型的全限定名 -->
        <!-- 主键应该使用id节点进行配置,非主键、非集合的使用result节点进行配置 -->
        <!-- column=结果集中的列名,property=属性名 -->
        <!-- 在关联查询中,即便结果集中的列名与类的属性名完全相同,也必须配置 -->
        <!-- collection子节点:用于配置1对多关系的数据部分,通常在类中是List类型的属性 -->
        <!-- collection子节点的ofType:List集合中的元素的类型 -->
        <resultMap id="DetailsResultMap" type="cn.tedu.mybatis.vo.AdminDetailsVO">
            <id column="id" property="id" />
            <result column="username" property="username" />
            <result column="password" property="password" />
            <result column="nickname" property="nickname" />
            <result column="avatar" property="avatar" />
            <result column="phone" property="phone" />
            <result column="email" property="email" />
            <result column="description" property="description" />
            <result column="is_enable" property="isEnable" />
            <result column="last_login_ip" property="lastLoginIp" />
            <result column="login_count" property="loginCount" />
            <result column="gmt_last_login" property="gmtLastLogin" />
            <result column="gmt_create" property="gmtCreate" />
            <result column="gmt_modified" property="gmtModified" />
            <collection property="roles" ofType="cn.tedu.mybatis.entity.Role">
                <id column="role_id" property="id" />
                <result column="role_name" property="name" />
                <result column="role_description" property="description" />
                <result column="role_sort" property="sort" />
                <result column="role_gmt_create" property="gmtCreate" />
                <result column="role_gmt_modified" property="gmtModified" />
            </collection>
        </resultMap>
    
        <resultMap id="BaseResultMap" type="cn.tedu.mybatis.entity.Admin">
            <id column="id" property="id" />
            <result column="username" property="username" />
            <result column="password" property="password" />
            <result column="nickname" property="nickname" />
            <result column="avatar" property="avatar" />
            <result column="phone" property="phone" />
            <result column="email" property="email" />
            <result column="description" property="description" />
            <result column="is_enable" property="isEnable" />
            <result column="last_login_ip" property="lastLoginIp" />
            <result column="login_count" property="loginCount" />
            <result column="gmt_last_login" property="gmtLastLogin" />
            <result column="gmt_create" property="gmtCreate" />
            <result column="gmt_modified" property="gmtModified" />
        </resultMap>
    
    </mapper>
    
    

     

  • 4.小结

    1.了解

  • 了解如何创建一个整合了Spring框架的Mybatis工程

  • 了解整合了Spring框架的Mybatis工程的配置

  • 了解使用注解配置sql语句

  • 掌握使用封装sql语句片段,并使用进行引用,以实现sql语句的复用

  • 理解resultType与resultMap使用原则

  • 尽可能的全部使用resultMap,如果查询结果是单一某个数据类型(例如基本数据类型或字符串或某个时间等),则使用resultType

  • 2.掌握

  • 掌握声明抽象方法的原则:

  • 返回值类型:增删改类型的操作均返回int,表示“受影响的行数”,查询类型操作,根据操作得到的结果集来决定,只要能够放入所有所需的数据即可,通常,统计查询返回int,查询最多1个结果时返回自定义的某个数据类型,查询多个结果时返回List集合类型

  • 方法名称:自定义,不要使用重载,其它命名建议参考此前的笔记

  • 参数列表:根据需要执行的sql语句中的参数来设计,并且,当需要执行的是插入数据操作时,必须将这些参数进行封装,并在封装的类中添加主键属性,以便于Mybatis能获取自动编号的值回填到此主键属性中,当需要执行的是其它类型的操作时,如果参数数量较多,可以封装,如果只有1个,则直接声明为方法参数,如果超过1个且数量不多,则每个参数之前添加@Param注解

  • 掌握使用XML配置sql语句

  • 这类XML文件需要顶部特殊的声明,所以,通常是从网上下载或通过复制粘贴得到此类文件

  • 根节点必须是,且必须配置namespace,取值为对应的Java接口的全限定名

  • 应该根据要执行的sql语句不同来选择、、、节点,这些节点都必须配置id属性,取值为对应的抽象方法的名称

  • 其实,节点和节点可以随意替换使用,但不推荐

  • 在不考虑“获取自动生成的主键值”的情况下,和、也可以混为一谈,但不推荐

  • 当插入数据时,当需要获取自动生成的主键值时,需要在节点上配置useGeneratedKeys和keyProperty属性

  • 在节点上,必须配置resultMap或resultType属性中的其中1个

  • 掌握的配置方式

  • 主键列与属性的映射必须使用节点配置

  • 在1对多、多对多的查询中,集合类型的属性的映射必须使用子节点配置

  • 其它列与属性的映射使用节点配置

  • 在单表查询中,列与属性名一致时,可以不必显式的配置,但是,在关联查询中,即使列与属性名称一致,也必须显式的配置出来

  • 掌握动态sql中的``的使用

  • 5.课后阅读

    1. 关于#{}和${}格式的占位符

  • 在Mybatis中,配置sql语句时,参数可以使用#{}或${}格式的占位符

  • 例如存在需求:分页查询表中的所有数据。

  • 需要执行的sql语句大致是:

  • select * from ams_admin order by id limit ?, ?
    
  • 则此功能的抽象方法应该是:

  • List<Admin> listPage(@Param("offset") Integer offset,
    @Param("size") Integer size);
    
  • 配置sql语句:

  • <select id="listPage" resultMap="BaseResultMap">
    select
    <include refid="BaseQueryFields" />
    from ams_admin
    order by id
    limit #{offset}, #{size}
    </select>
    
  • 执行测试:

  • @Test
    public void testListPage() {
    Integer offset = 0;
    Integer size = 3;
    List<Admin> adminList = adminMapper.listPage(offset, size);System.out.println("查询到的记录数:" + adminList.size());
    for (Admin admin : adminList) {
    System.out.println(admin);
    }
    }
    
  • 以上代码可以正常通过测试,并且观察结果也都是符合预期的,即使把sql语句中的#{}换成${}格式,也是完全没有问题的!

  • 例如还存在需求:根据用户名查询此用户的详情

  • 在“根据用户名查询用户详情”时,如果将username=#{username}换成username=${username}会出现错误!

  • 其实,使用#{}格式的占位符时,Mybatis在处理时会使用预编译的做法,所以,在编写sql语句时不必关心数据类型的问题(例如字符串值不需要添加单引号),也不存在sql注入的风险!这种占位符只能用于表示某个值,而不能表示sql语句片段!

  • 当使用${}格式的占位符时,Mybatis在处理时会先将参数值代入到sql语句中,然后再执行编译相关过程,所以需要关心某些值的数据类型问题(例如涉及字符串值时,需要在编写sql语句时添加一对单引号框住字符串),并且,存在sql注入的风险!其优点是可以表示sql语句中的任何片段!

  • 在一般情况下,应该尽可能的使用#{}格式的占位符,并不推荐使用${}格式的占位符,即使它可以实现“泛用”的效果!

  • 在一些特殊的情况下,如果一定要使用${}格式的占位符,必须考虑sql注入的风险,应该使用正则表达式或其它做法避免出现sql注入问题!

  • 2. Mybatis的缓存机制

    1.缓存

  • 通常是一个临时存储的数据,在未来的某个时间点可能会被删除

  • 通常,存储缓存数据的位置是读写效率较高的,相比其它“非缓存”的数据有更高的处理效率

  • 由于缓存的数据通常并不是必须的,则需要额外消耗一定的存储空间,同时由于从缓存获取数据的效率更高,所以是一种牺牲空间、换取时间的做法

  • 另外,你必须知道,从数据库读取数据的效率是非常低下的

  • 2.两种缓存机制

  • 一级缓存是基于sqlSession的缓存,也称之为“会话缓存”,仅当是同一个会话、同一个Mapper、同一个抽象方法(同一个sql语句)、同样的参数值时有效,一级缓存在集成框架的应用中默认是开启的,且整个过程不由人为控制(如果是自行得到sqlSession后的操作,可自行清理一级缓存)

  • 二级缓存默认是全局开启的,它是基于namespace的,所以也称之为“ namespace缓存” ,需要在配置sql语句的XML中添加节点,以表示当前XML中的所有查询都允许开通二级缓存,并且,在节点上配置useCache= "true" ,则对应的节点的查询结果将被二级缓存处理,并且,此查询返回的结果的类型必须是实现了Serializable接口的,如果使用了配置如何封装查询结果,则必须使用节点来封装主键的映射,满足以上条件后,二级缓存将可用,只要是当前namespace中查询出来的结果,都会根据所执行的sql语句及参数进行结果的缓存

  • 无论是一级缓存还是二级缓存,只要数据发生了写操作(增、删、改),缓存数据都将被自动清理

  • 由于Mybatis的缓存清理机制过于死板,所以,一般在开发实践中并不怎么使用!更多的是使用其它的缓存工具并自行制定缓存策略

  •  
  • java web(七): mybatis的动态sql和mybatis generator自动生成pojo类和映射文件

    java web(七): mybatis的动态sql和mybatis generator自动生成pojo类和映射文件

    前言:

      MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据

    不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。

    利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

      Mybatis Generator可以帮我们根据数据库表自动生成pojo类和SQL映射文件,SQL映射文件提供了增删改查功能。

     


             动态SQL

    到网上找一个经典的mysql数据库表

    CREATE TABLE `emp` (
      #编号
      `empno` int(11) NOT NULL,
      #姓名
      `ename` varchar(255) NOT NULL,
      #职位
      `job` varchar(255) DEFAULT NULL,
      `mgr` int(11) DEFAULT NULL,
      #入职时间
      `hiredate` date NOT NULL,
      #薪水
      `sal` decimal(7,2) DEFAULT NULL,
      #奖金级别
      `comm` decimal(7,2) DEFAULT NULL,
      #部门编号
      `deptno` int(11) DEFAULT NULL,
      PRIMARY KEY (`empno`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

     

    #部门表
    CREATE TABLE `dept` (
      #部门编号
      `deptno` int(11) NOT NULL,
      #部门名称
      `dname` varchar(255) NOT NULL,
      #部门地址
      `loc` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`deptno`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

     

    INSERT INTO emp VALUES(7369,''SMITH'',''CLERK'',7902,''1980-12-17'',800,NULL,20);
    INSERT INTO emp VALUES(7499,''ALLEN'',''SALESMAN'',7698,''1981-02-20'',1600,300,30);
    INSERT INTO emp VALUES(7521,''WARD'',''SALESMAN'',7698,''1981-02-22'',1250,500,30);
    INSERT INTO emp VALUES(7566,''JONES'',''MANAGER'',7839,''1981-04-02'',2975,NULL,20);
    INSERT INTO emp VALUES(7654,''MARTIN'',''SALESMAN'',7698,''1981-09-28'',1250,1400,30);
    INSERT INTO emp VALUES(7698,''BLAKE'',''MANAGER'',7839,''1981-05-01'',2850,NULL,30);
    INSERT INTO emp VALUES(7782,''CLARK'',''MANAGER'',7839,''1981-06-09'',2450,NULL,10);
    INSERT INTO emp VALUES(7788,''SCOTT'',''ANALYST'',7566,''1987-04-19'',3000,NULL,20);
    INSERT INTO emp VALUES(7839,''KING'',''PRESIDENT'',NULL,''1981-11-17'',5000,NULL,10);
    INSERT INTO emp VALUES(7844,''TURNER'',''SALESMAN'',7698,''1981-09-08'',1500,0,30);
    INSERT INTO emp VALUES(7876,''ADAMS'',''CLERK'',7788,''1987-05-23'',1100,NULL,20);
    INSERT INTO emp VALUES(7900,''JAMES'',''CLERK'',7698,''1981-12-03'',950,NULL,30);
    INSERT INTO emp VALUES(7902,''FORD'',''ANALYST'',7566,''1981-12-03'',3000,NULL,20);
    INSERT INTO emp VALUES(7934,''MILLER'',''CLERK'',7782,''1982-01-23'',1300,NULL,10);

     
    INSERT INTO dept VALUES(10, ''ACCOUNTING'', ''NEW YORK'');
    INSERT INTO dept VALUES(20, ''RESEARCH'', ''DALLAS'');
    INSERT INTO dept VALUES(30, ''SALES'', ''CHICAGO'');
    INSERT INTO dept VALUES(40, ''OPERATIONS'', ''BOSTON'');

     


     

    表准备完成之后,开始编写动态SQL语句

    If

    动态 SQL 通常要做的事情是根据条件包含 where 子句的一部分。

    <select id="selectEmployeeByCondition1" parameterType="Employee"
                resultMap="EmployeeBaseMap">
            select * from emp
            where 1 = 1
            <if test="empno != null">
                and empno = #{empno}
            </if>
            <if test="ename != null">
                and ename like #{ename}
            </if>
            <if test="job != null">
                and job = #{job}
            </if>
    </select>

     这条语句会根据empno,ename,job是否为空插入条件, " where 1 =  1 "是为了避免3个条件都为空出现"select * from emp where "这种SQL语句。

    每个if条件之间必须显式用and或or进行连接。

     

    choose,when,otherwise

    有时我们不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了 choose 元素

    choose类似if else if  else,只会选择中一个条件执行。

      <!-- choose -->
        <select id="selectEmployeeByCondition2" parameterType="Employee" 
                resultMap="EmployeeBaseMap">
            select * from emp
            where
            <choose>
                <when test="empno != null">
                    empno = #{empno}
                </when>
                <when test="ename">
                    ename like #{ename}
                </when>
                <when test="job != null">
                    job = #{job}
                </when>
                <otherwise>
                    1 = 1
                </otherwise>
            </choose>
        </select>

     如果三个when条件都不满足,则会选中<otherwise>拼接到where条件后面!

     

    where   set   trim

    上面的几条动态SQL语句都会加上where条件,有时候我们希望如果条件都不满足的情况下不加where字句。

      <select id="selectEmployeeByCondition3" parameterType="Employee" 
                resultMap="EmployeeBaseMap">
            select * from emp
            <where>
                <if test="empno != null">
                    and empno = #{empno}
                </if>
                <if test="ename != null">
                    and ename like #{ename}
                </if>
                <if test="job != null">
                    and job = #{job}
                </if>
            </where>
      </select> 

     

    where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。

    而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。

     

    和where等价的trim

      <select id="selectEmployeeByCondition4" parameterType="Employee" 
                resultMap="EmployeeBaseMap">
            select * from emp
            <trim prefix="where" prefixOverrides="and | or">
                <if test="empno != null">
                    and empno = #{empno}
                </if>
                <if test="ename != null">
                    and ename like #{ename}
                </if>
                <if test="job != null">
                    and job = #{job}
                </if>
            </trim>
        </select> 

     

     <trim> 标签有两个属性,prefix是指定插入的内容,prefixOverrides属性会忽略通过管道分隔的文本序列。

    它的作用是移除所有指定在 prefixOverrides 属性中的内容,并且插入 prefix 属性中指定的内容。

    假设一和三条件满足,SQL语句就会变成 " select * from emp where empno = #{empno} and job = #{job} ";

     

    <set>标签是专门为更新语句而准备的。

      <update id="updateEmployee" parameterType="Employee">
            update emp
            <set>
                <if test="ename != null">
                    ename = #{ename},
                </if>
                <if test="job != null">
                    job = #{job},
                </if>
                <if test="mgr != null">
                    mgr = #{mgr},
                </if>
                <if test="salary != null">
                    sal = #{salary},
                </if>
                <if test="comment != null">
                    comm = #{comment},
                </if>
                <if test="dept != null and dept.deptNo != null">
                    deptno = #{dept.deptNo}
                </if>
            </set>
            where empno = #{empno}
        </update>

     set 元素可以用于动态包含需要更新的列,而舍去其它的

    这里,set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,

    因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号。

    当然trim也可以完成set标签的功能

      <update id="updateEmployee2" parameterType="Employee">
            update emp
            <trim prefix="set" prefixOverrides=",">
                <if test="ename != null">
                    ename = #{ename},
                </if>
                <if test="job != null">
                    job = #{job},
                </if>
                <if test="mgr != null">
                    mgr = #{mgr},
                </if>
                <if test="salary != null">
                    sal = #{salary},
                </if>
                <if test="comment != null">
                    comm = #{comment},
                </if>
                <if test="dept != null and dept.deptNo != null">
                    deptno = #{dept.deptNo}
                </if>
            </trim>
            where empno = #{empno}
        </update>

     

    forEach

    动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候。

      <select id="selectEmployeeByEmpnos" parameterType="list" resultMap="EmployeeBaseMap">
            select * from emp
            where empno in
            <foreach collection="list" open="(" close=")" item="item" index="index" separator=",">
                #{item}
            </foreach>
        </select>

     foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和

    索引(index)变量。它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符。

    这个元素是很智能的,因此它不会偶然地附加多余的分隔符。

    注意: 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。

    当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。

    当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

     

    动态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.briup.mapper.DynamicMapper">
        <!-- 告诉数据库如何加载数据到类的那个属性上 -->
        <resultMap type="Employee" id="EmployeeBaseMap">
            <id column="empno" property="empno"/>
            <result column="ename" property="ename"/>
            <result column="job" property="job"/>
            <result column="mgr" property="mgr"/>
            <result column="hiredate" property="hiredate"/>
            <result column="sal" property="salary"/>
            <result column="comm" property="comment"/>
        </resultMap>
        
        <resultMap type="Deptarment" id="DeptarmentBaseMap">
            <id column="deptno" property="deptNo"/>
            <result column="dname" property="dName"/>
            <result column="loc" property="loc"/>
        </resultMap>
        
        
        <!-- if -->
        <select id="selectEmployeeByCondition1" parameterType="Employee" 
                resultMap="EmployeeBaseMap">
            select * from emp
            where 1 = 1
            <if test="empno != null">
                and empno = #{empno}
            </if>
            <if test="ename != null">
                and ename like #{ename}
            </if>
            <if test="job != null">
                and job = #{job}
            </if>
        </select>
        
        <!-- choose -->
        <select id="selectEmployeeByCondition2" parameterType="Employee" 
                resultMap="EmployeeBaseMap">
            select * from emp
            where
            <choose>
                <when test="empno != null">
                    empno = #{empno}
                </when>
                <when test="ename">
                    ename like #{ename}
                </when>
                <when test="job != null">
                    job = #{job}
                </when>
                <otherwise>
                    1 = 1
                </otherwise>
            </choose>
        </select>
        
        <!-- where trim set -->
        <select id="selectEmployeeByCondition3" parameterType="Employee" 
                resultMap="EmployeeBaseMap">
            select * from emp
            <where>
                <if test="empno != null">
                    and empno = #{empno}
                </if>
                <if test="ename != null">
                    and ename like #{ename}
                </if>
                <if test="job != null">
                    and job = #{job}
                </if>
            </where>
        </select> 
        
        <select id="selectEmployeeByCondition4" parameterType="Employee" 
                resultMap="EmployeeBaseMap">
            select * from emp
            <trim prefix="where" prefixOverrides="and | or">
                <if test="empno != null">
                    and empno = #{empno}
                </if>
                <if test="ename != null">
                    and ename like #{ename}
                </if>
                <if test="job != null">
                    and job = #{job}
                </if>
            </trim>
        </select> 
        
        <update id="updateEmployee" parameterType="Employee">
            update emp
            <set>
                <if test="ename != null">
                    ename = #{ename},
                </if>
                <if test="job != null">
                    job = #{job},
                </if>
                <if test="mgr != null">
                    mgr = #{mgr},
                </if>
                <if test="salary != null">
                    sal = #{salary},
                </if>
                <if test="comment != null">
                    comm = #{comment},
                </if>
                <if test="dept != null and dept.deptNo != null">
                    deptno = #{dept.deptNo}
                </if>
            </set>
            where empno = #{empno}
        </update>
        
        <update id="updateEmployee2" parameterType="Employee">
            update emp
            <trim prefix="set" prefixOverrides=",">
                <if test="ename != null">
                    ename = #{ename},
                </if>
                <if test="job != null">
                    job = #{job},
                </if>
                <if test="mgr != null">
                    mgr = #{mgr},
                </if>
                <if test="salary != null">
                    sal = #{salary},
                </if>
                <if test="comment != null">
                    comm = #{comment},
                </if>
                <if test="dept != null and dept.deptNo != null">
                    deptno = #{dept.deptNo}
                </if>
            </trim>
            where empno = #{empno}
        </update>
        
        <select id="selectEmployeeByEmpnos" parameterType="list" resultMap="EmployeeBaseMap">
            select * from emp
            where empno in
            <foreach collection="list" open="(" close=")" item="item" index="index" separator=",">
                #{item}
            </foreach>
        </select>
    </mapper>
    View Code

     映射接口:

    package com.briup.mapper;
    
    import java.util.List;
    
    import com.briup.bean.Employee;
    
    public interface DynamicMapper {
        
        public abstract List<Employee> selectEmployeeByCondition1(Employee emp);
        
        public abstract List<Employee> selectEmployeeByCondition2(Employee emp);
        
        public abstract List<Employee> selectEmployeeByCondition3(Employee emp);
        
        public abstract List<Employee> selectEmployeeByCondition4(Employee emp);
        
        public abstract void updateEmployee(Employee emp);
        
        public abstract List<Employee> selectEmployeeByEmpnos(List<Integer> empnos);
    }
    View Code

     

     


     

    mybatis Generator在ecplise中的使用

    mybatis生成器(mbg)是mybatis mybatis和ibatis的代码生成器。它将为mybatis的所有版本生成代码,

    并在版本2.2.0之后生成ibatis的版本。它将内省一个数据库表(或多个表),并生成可用于访问表的工件。

    这减少了设置对象和配置文件以与数据库表交互的初始麻烦。MBG试图对大部分简单的CRUD(创建、检索、更新、删除)

    数据库操作产生重大影响。您仍然需要为连接查询或存储过程手工编写SQL和对象代码。

    MyBatis Generator will generate:

             *Java POJOs that match the table structure.

             *MyBatis/iBATIS Compatible SQL Map XML Files. MBG generates SQL for simple CRUD    functions on each table in a configuration.

             *Java client classes that make appropriate use of the above objects.

     

    1.安装Mybatis Generator插件

      help --> Ecplise Marketplace...

    然后搜索mybats,点击安装Mybatis Generator 1.3.7

     

    2.创建配置文件

      右键点击java项目,选择 " New -- > Other... " 然后搜索mybatis,选中Mybatis Generator configuration File。

    3.准备一些包和目录

    在项目下创建一个目录lib,把连接mysql数据库的jar包放进去,我这里是msql-connector-java-5.1.47.jar

    创建包com.briup.bean 和 com.briup.mapper,等下自动生成的pojo类和映射文件会放到这下面。

    4.修改配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
      PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
      "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    <!-- mybatis-generator的核心配置文件 -->
    <generatorConfiguration>
        <!-- 连接数据库用到的jar包 -->
        <classPathEntry
            location="G:\java-code-2\mybatis-generator\lib\mysql-connector-java-5.1.47.jar" />
    
        <context id="DB2Tables" targetRuntime="MyBatis3">
            <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                connectionURL="jdbc:mysql://188.131.246.182:3306/cnblogs" userId="study"
                password="123456">
                <!-- 生成primary key方法 -->
                <property name="userInformationSchema" value="true" />
            </jdbcConnection>
    
            <!--指定生成的类型为java类型,避免数据库中number等类型字段 -->
            <javaTypeResolver>
                <property name="forceBigDecimals" value="false" />
            </javaTypeResolver>
    
            <!--自动生成的实体的存放包路径 -->
            <javaModelGenerator targetPackage="com.briup.bean"
                targetProject="mybatis-generator/src">
                <property name="enableSubPackages" value="true" />
                <property name="trimStrings" value="true" />
            </javaModelGenerator>
    
            <!--自动生成的*Mapper.xml文件存放路径 -->
            <sqlMapGenerator targetPackage="com.briup.mapper"
                targetProject="mybatis-generator/src">
                <property name="enableSubPackages" value="true" />
            </sqlMapGenerator>
    
            <!--自动生成的*Mapper.java存放路径 -->
            <javaClientGenerator type="XMLMAPPER"
                targetPackage="com.briup.mapper" targetProject="mybatis-generator/src">
                <property name="enableSubPackages" value="true" />
            </javaClientGenerator>
    
    
            <!-- 映射配置 -->
            <table tableName="emp" domainObjectName="Employee"></table>
            <table tableName="dept" domainObjectName="Deptarment"></table>
        </context>
    </generatorConfiguration>

     5.运行配置文件

    右键点击generatorConfig.xml文件,选择 ''Run As'' , ''Run Mybatis Generator''。

     

    6.简单的介绍如何使用Mybatis Generator生成的映射文件进行增删改查。

    如果你去看xml文件,会发现里面使用了动态的SQL语句,不过又额外的增加了一些东西。有兴趣的可以研究一下。

    使用之前还需要把环境搭建好,什么mybatis-config.xml全局配置文件以及其他的都要准备好,上一篇随笔有如何在ecplise搭建一个mybatis项目。

    由于自动生成的pojo类中toString都没有重写,不好看结果,我利用ecplise自动生成toString方法。

     

    0)使用案例0

    @SuppressWarnings("unused")
        @Test
        public void test0() {
            SqlSession session = null;
            try {
                session = MySqlSessionFactory.opensession();
                EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
                
                // 查询所有员工信息
                EmployeeExample example = new EmployeeExample();
                List<Employee> list = mapper.selectByExample(example);
                System.out.println("list.size(): " + list.size());
                for(Employee e: list)
                    System.out.println("name: " + e.getEname());
            } catch (IOException e) {
                if(session != null)
                    session.close();
                e.printStackTrace();
            }
        }
    View Code

    1)使用案列1

    查询工资大于1200的员工姓名和工资

    @SuppressWarnings("unused")
        @Test
        public void test1() {
            SqlSession session = null;
            try {
                session = MySqlSessionFactory.opensession();
                EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
                
                // 查询工资大于1200的员工姓名和工资 
                EmployeeExample example = new EmployeeExample();
                Criteria criteria = example.createCriteria();
                // 添加查询条件
                criteria.andSalGreaterThan(new BigDecimal(1200));
                List<Employee> list = mapper.selectByExample(example);
                System.out.println("list.size(): " + list.size() + " and list: " + list );
            } catch (IOException e) {
                if(session != null)
                    session.close();
                e.printStackTrace();
            }
        }
    View Code

      2)使用案列2

    选择工资不在500到1200的员工的姓名和工资

    @SuppressWarnings("unused")
        @Test
        public void test2() {
            SqlSession session = null;
            try {
                session = MySqlSessionFactory.opensession();
                EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
                
                // 选择工资不在500到1200的员工的姓名和工资
                EmployeeExample example = new EmployeeExample();
                Criteria criteria = example.createCriteria();
                // 添加查询条件
                criteria.andSalNotBetween(new BigDecimal(500), new BigDecimal(1200));
                List<Employee> list = mapper.selectByExample(example);
                System.out.println("list.size(): " + list.size());
                for(Employee e: list)
                    System.out.println("salary: " + e.getSal());
            } catch (IOException e) {
                if(session != null)
                    session.close();
                e.printStackTrace();
            }
        }
    View Code

     3)使用案例3

    选择姓名中有字母a和e的员工

    @SuppressWarnings({ "unused" })
        @Test
        public void test3() {
            SqlSession session = null;
            try {
                session = MySqlSessionFactory.opensession();
                EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
                
                // 选择姓名中有字母a和e的员工
                EmployeeExample example = new EmployeeExample();
                Criteria criteria = example.createCriteria();
                // 添加查询条件
                criteria.andEnameLike("%a%");
                criteria.andEnameLike("%e%");
                List<Employee> list = mapper.selectByExample(example);
                System.out.println("list.size(): " + list.size());
                for(Employee e: list)
                    System.out.println("name: " + e.getEname());
            } catch (IOException e) {
                if(session != null)
                    session.close();
                e.printStackTrace();
            }
        }
    View Code

     

      注:如何需要联合查询还需要自己写sql语句,mybatsi Generator生成的SQL映射文件只提供增删改查功能。

    备注:mybais generator项目已经在百度网盘分享。

    地址:https://pan.baidu.com/s/1ZrFsu1CcMtrBCmvk9y5IDQ

    提取码:2tk9

     

    JavaEE_Mybatis_SpringMVC__Mybatis_lesson8_Mybatis的动态sql

    JavaEE_Mybatis_SpringMVC__Mybatis_lesson8_Mybatis的动态sql

    项目代码

    http://pan.baidu.com/s/1c01BLvi


    Mybatis中  拼接动态SQL,让SQL根据传递进来的参数,进行动态的拼接,拼成指定条件的语句


    0.配置文件

    1.userMapper.java (Dao接口)

    2.userMapper.xml  (Dao实现)   1,2是通过配置文件中 批量加载mapper 的方式联系的。(package的方式) 

    注意

    (1) xxxMapper.xml 需要与 xxxMapper.java 名称相同,

    (2)且放到同一文件夹下面。

    动态SQL写在 userMapper.xml 里面:

      通过类似于JSTL标签的表现方式

    <!-- where 可以自动去掉第一个and  -->

    	<!--
    	通过OGNL的方式进行获取 
    	 -->
    	<select id="findUserList" parameterType="cn.itcast.mybatis.po.UserQueryVo"
    	resultType="cn.itcast.mybatis.po.UserCustom">
    		SELECT * FROM user 
    		<!--
    		where 可以自动去掉第一个and 
    		 -->
    		<where>
    			<if test="userCustom!=null">
    				<if test="userCustom.sex!=null and userCustom.sex!=''">
    					AND user.sex = #{userCustom.sex}
    				</if>
    				<if test="userCustom.username!=null and userCustom.username!=''">
    					AND user.username like'%${userCustom.username}%'
    				</if>
    			</if>
    		</where>
    	</select>



    3.User.java (ORM)

    4.UserCustom.java(po类)

    5.UserQueryVo(vo类)

    tips:

    (1) Vo 视图层面的对象

    (2) Po 持久层面的对象

    (3) Pojo 用户自定义的对象,综合了Vo与Po的JavaBean

    6.junit 单元测试




    文档目录结构



    0.配置文件

    db.properties

    [plain]  view plain copy print ?
    1. jdbc.driver=com.mysql.jdbc.Driver  
    2. jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8  
    3. jdbc.username=root  
    4. jdbc.password=123456  



    SqlMapConfig.xml

    [plain]  view plain copy print ?
    1. <?xml version="1.0" encoding="UTF-8" ?>  
    2. <!DOCTYPE configuration  
    3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
    4. "http://mybatis.org/dtd/mybatis-3-config.dtd">  
    5. <configuration>  
    6.     <!-- 加载属性文件 -->  
    7.     <properties resource="db.properties"></properties>  
    8.     <!--   
    9.     <settings></settings>  
    10.      -->  
    11.     <typeAliases>  
    12.         <!--  
    13.         <typeAlias type="cn.itcast.mybatis.po.User" alias="user"/>   
    14.          -->  
    15.          <!-- 批量定义别名 -->  
    16.         <package name="cn.itcast.mybatis.po"/>  
    17.     </typeAliases>  
    18.     <!-- 和spring整合后 environments配置将废除-->  
    19.     <environments default="development">  
    20.         <environment id="development">  
    21.         <!-- 使用jdbc事务管理-->  
    22.             <transactionManager type="JDBC" />  
    23.         <!-- 数据库连接池-->  
    24.             <dataSource type="POOLED">  
    25.                 <property name="driver" value="${jdbc.driver}" />  
    26.                 <property name="url" value="${jdbc.url}" />  
    27.                 <property name="username" value="${jdbc.username}" />  
    28.                 <property name="password" value="${jdbc.password}" />  
    29.             </dataSource>  
    30.         </environment>  
    31.     </environments>  
    32.     <mappers>  
    33.         <mapper resource="sqlmap/User.xml"/>  
    34.         <!--   
    35.         <mapper resource="mapper/UserMapper.xml"/>  
    36.          -->  
    37.         <!--  
    38.         <mapper />   
    39.          -->  
    40.         <package name="cn.itcast.mybatis.mapper"/>  
    41.           
    42.     </mappers>  
    43. </configuration>  


    1.userMapper.java (Dao接口)

    [plain]  view plain copy print ?
    1. package cn.itcast.mybatis.mapper;  
    2.   
    3. import java.util.List;  
    4.   
    5. import cn.itcast.mybatis.po.User;  
    6. import cn.itcast.mybatis.po.UserCustom;  
    7. import cn.itcast.mybatis.po.UserQueryVo;  
    8.   
    9. public interface UserMapper {  
    10.   
    11.     List<UserCustom> findUserList(UserQueryVo userQueryVo) throws Exception;  
    12.   
    13.     User findUserById(Integer id) throws Exception;  
    14.   
    15.     public List<User> findUserByName(String name) throws Exception;  
    16.   
    17.     void insertUser(User user) throws Exception;  
    18.   
    19.     void deleteUser(int id) throws Exception;  
    20.   
    21. }  


    2.userMapper.xml  (Dao实现)

    <?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有特殊重要的作用-->
    <mapper namespace="cn.itcast.mybatis.mapper.UserMapper">
    
    	<resultMap type="User" id="userResultMap">
    		<id column="_id" property="id"/>
    		<result column="_username" property="username"/>
    	</resultMap>
    	
    	<!--
    	通过OGNL的方式进行获取 
    	 -->
    	<select id="findUserList" parameterType="cn.itcast.mybatis.po.UserQueryVo"
    	resultType="cn.itcast.mybatis.po.UserCustom">
    		SELECT * FROM user 
    		<!--
    		where 可以自动去掉第一个and 
    		 -->
    		<where>
    			<if test="userCustom!=null">
    				<if test="userCustom.sex!=null and userCustom.sex!=''">
    					AND user.sex = #{userCustom.sex}
    				</if>
    				<if test="userCustom.username!=null and userCustom.username!=''">
    					AND user.username like'%${userCustom.username}%'
    				</if>
    			</if>
    		</where>
    	</select>
    
    	<select id="findUserCount" parameterType="cn.itcast.mybatis.po.UserQueryVo"
    	resultType="int">
    		SELECT COUNT(*) FROM user WHERE user.sex = #{userCustom.sex} AND user.username like'%${userCustom.username}%'
    		<!--
    		where 可以自动去掉第一个and 
    		 -->
    		<where>
    			<if test="userCustom!=null">
    				<if test="userCustom.sex!=null and userCustom.sex!=''">
    					AND user.sex = #{userCustom.sex}
    				</if>
    				<if test="userCustom.username!=null and userCustom.username!=''">
    					AND user.username like'%${userCustom.username}%'
    				</if>
    			</if>
    		</where>
    	</select>
    	
    	<!-- 利用resultMap作为返回值返回参数 -->
    	<select id="findUserByIdResultMap" parameterType="java.lang.Integer" resultMap="userResultMap">
    	 	SELECT id _id, username _username FROM user where id = #{id} 
    	</select>
    	
    	<!-- 在 映射文件中配置很多sql语句 -->
    	<!-- 需求:通过id查询用户表的记录 -->
    	<!-- 通过 select执行数据库查询
    	id:标识 映射文件中的 sql
    	将sql语句封装到mappedStatement对象中,
    	=====================所以将id称为statement的id
    	parameterType:指定输入 参数的类型,这里指定int型 
    	#{}表示一个占位符号
    	
    	#{id}:其中的
    	====================id表示接收输入 的参数,参数名称就是id,
    	====================如果输入 参数是简单类型,#{}中的参数名可以任意,可以value或其它名称
    	#{},
    	resultType:指定sql输出结果 的所映射的java对象类型,select指定resultType表示将单条记录映射成的java对象。
    	 -->
    	 <!-- cn.itcast.mybatis.po.User -->
    	 <select id="findUserById" parameterType="java.lang.Integer" resultType="user">
    	 	SELECT * FROM user where id = #{id} 
    	 </select>
    	 
    	 
    	 
    	<!-- 根据用户名称模糊查询用户信息,可能返回多条
    	resultType:指定就是单条记录所映射的java对象 类型
    	${}:表示拼接sql串,将接收到参数的内容不加任何修饰拼接在sql中。
    	=============使用${}拼接sql,引起 sql注入
    	=============${value}:接收输入 参数的内容,如果传入类型是简单类型,${}中只能使用value
    	 --> 
    	 <select id="findUserByName" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User">
    	 	SELECT * FROM user where username like '%${value}%'
    	 </select>
    	 
    	 
    	 <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
    	 	<!-- 
    		将插入数据的主键返回,返回到user对象中
    		
    		=================SELECT LAST_INSERT_ID():得到刚insert进去记录的主键值,只适用与自增主键
    		
    		keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性
    		orderSELECT LAST_INSERT_ID()执行顺序,相对于insert语句来说它的执行顺序
    		resultType:指定SELECT LAST_INSERT_ID()的结果类型
    		 -->
    	 	<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
    	 		SELECT LAST_INSERT_ID()
    	 	</selectKey>
    	 	INSERT INTO user(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address}) 
    	 	<!-- 
    		使用mysql的uuid()生成主键
    		执行过程:
    		首先通过uuid()得到主键,将主键设置到user对象的id属性中
    		其次在insert执行时,从user对象中取出id属性值
    		 -->
    		<!--  <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
    			SELECT uuid()
    		</selectKey>
    		insert into user(id,username,birthday,sex,address) value(#{id},#{username},#{birthday},#{sex},#{address}) -->
    	 </insert>
    	 	
    	<!-- 删除 用户
    	根据id删除用户,需要输入 id值
    	 -->
    	<delete id="deleteUser" parameterType="java.lang.Integer">
    		DELETE FROM user WHERE id=#{id}
    	</delete>
    	 
    	<!-- 根据id更新用户
    	分析:
    	需要传入用户的id
    	需要传入用户的更新信息
    	parameterType指定user对象,包括 id和更新信息,注意:id必须存在
    	#{id}:从输入 user对象中获取id属性值
    	 -->
    	<update id="updateUser"	parameterType="cn.itcast.mybatis.po.User">
    		UPDATE user SET username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} 
    		WHERE id=#{id}
    	</update>
    	 
    </mapper>



    3.User.java (ORM)

    [java]  view plain copy print ?
    1. package cn.itcast.mybatis.po;  
    2.   
    3. import java.util.Date;  
    4.   
    5. //hibernate字段名和属性名相对应  
    6. public class User {  
    7.     @Override  
    8.     public String toString() {  
    9.         return "User [id=" + id + ", username=" + username + ", birthday="  
    10.                 + birthday + ", sex=" + sex + ", address=" + address + "]";  
    11.     }  
    12.   
    13.     private int id;  
    14.     private String username;  
    15.     private Date birthday;  
    16.     private String sex;  
    17.     private String address;  
    18.   
    19.     public int getId() {  
    20.         return id;  
    21.     }  
    22.   
    23.     public void setId(int id) {  
    24.         this.id = id;  
    25.     }  
    26.   
    27.     public String getUsername() {  
    28.         return username;  
    29.     }  
    30.   
    31.     public void setUsername(String username) {  
    32.         this.username = username;  
    33.     }  
    34.   
    35.     public Date getBirthday() {  
    36.         return birthday;  
    37.     }  
    38.   
    39.     public void setBirthday(Date birthday) {  
    40.         this.birthday = birthday;  
    41.     }  
    42.   
    43.     public String getSex() {  
    44.         return sex;  
    45.     }  
    46.   
    47.     public void setSex(String sex) {  
    48.         this.sex = sex;  
    49.     }  
    50.   
    51.     public String getAddress() {  
    52.         return address;  
    53.     }  
    54.   
    55.     public void setAddress(String address) {  
    56.         this.address = address;  
    57.     }  
    58.   
    59. }  



    4.UserCustom.java(po类)

    [java]  view plain copy print ?
    1. package cn.itcast.mybatis.po;  
    2.   
    3. /** 
    4.  * <p> 
    5.  * Description:用户的扩展类 
    6.  * </p> 
    7.  *  
    8.  * @author szh 
    9.  */  
    10. public class UserCustom extends User {  
    11.     // 可以扩展用户的信息  
    12. }  




    5.UserQueryVo(vo类)

    [java]  view plain copy print ?
    1. package cn.itcast.mybatis.po;  
    2.   
    3. /** 
    4.  *  
    5.  * @author szh 
    6.  *  
    7.  */  
    8. public class UserQueryVo {  
    9.     private UserCustom userCustom;  
    10.   
    11.     public UserCustom getUserCustom() {  
    12.         return userCustom;  
    13.     }  
    14.   
    15.     public void setUserCustom(UserCustom userCustom) {  
    16.         this.userCustom = userCustom;  
    17.     }  
    18.   
    19.     // 可以包装其他的查询条件,订单,商品  
    20. }  


    6.junit 单元测试

    package cn.itcast.mybatis.mapper;
  • import java.io.InputStream;
  • import java.util.List;
  • import org.apache.ibatis.io.Resources;
  • import org.apache.ibatis.session.SqlSession;
  • import org.apache.ibatis.session.SqlSessionFactory;
  • import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  • import org.junit.Before;
  • import org.junit.Test;
  • import cn.itcast.mybatis.po.User;
  • import cn.itcast.mybatis.po.UserCustom;
  • import cn.itcast.mybatis.po.UserQueryVo;
  • public class UserMapperTest {
  • private SqlSessionFactory sqlSessionFactory;
  • // 此方法是在执行testFindUserById之前执行
  • @Before
  • public void setUp() throws Exception {
  • String resource = null; // mybatis全局配置文件
  • InputStream inputStream = null; // 输入流
  • try {
  • // mybatis配置文件
  • resource = "SqlMapConfig.xml";
  • inputStream = Resources.getResourceAsStream(resource);
  • // 创建会话工厂,传入mybatis配置文件信息
  • // 创建sqlSessionFactory
  • this.sqlSessionFactory = new SqlSessionFactoryBuilder()
  • .build(inputStream);
  • } catch (Exception e) {
  • e.printStackTrace();
  • }
  • }
  • @Test
  • public void testFindUserById() throws Exception {
  • SqlSession sqlSession = sqlSessionFactory.openSession();
  • UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  • User user = userMapper.findUserById(1);
  • System.out.println(user);
  • sqlSession.close();
  • }
  • @Test
  • public void testFindUserByIdResultMap() throws Exception {
  • SqlSession sqlSession = sqlSessionFactory.openSession();
  • UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  • User user = userMapper.findUserByIdResultMap(1);
  • System.out.println(user);
  • sqlSession.close();
  • }
  • @Test
  • public void testFindUserByName() throws Exception {
  • SqlSession sqlSession = sqlSessionFactory.openSession();
  • // 创建UserMapper对象,mybatis自动生成mapper代理对象
  • UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  • List<User> userList = userMapper.findUserByName("小");
  • System.out.println(userList);
  • sqlSession.close();
  • }
  • @Test
  • public void testFindUserList() throws Exception {
  • SqlSession sqlSession = null;
  • try {
  • sqlSession = sqlSessionFactory.openSession();
  • // 创建UserMapper对象,mybatis 自动生成mapper代理对象
  • UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  • UserQueryVo userQueryVo = new UserQueryVo();
  • UserCustom userCustom = new UserCustom();
  • // userCustom.setSex("1");
  • // userCustom.setUsername("小");
  • userQueryVo.setUserCustom(userCustom);
  • // userQueryVo
  • List<UserCustom> list = userMapper.findUserList(null);
  • System.out.println(list);
  • } catch (Exception e) {
  • e.printStackTrace();
  • } finally {
  • sqlSession.close();
  • }
  • }
  • @Test
  • public void testFindUserCount() throws Exception {
  • SqlSession sqlSession = null;
  • try {
  • sqlSession = sqlSessionFactory.openSession();
  • // 创建UserMapper对象,mybatis 自动生成mapper代理对象
  • UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  • UserQueryVo userQueryVo = new UserQueryVo();
  • UserCustom userCustom = new UserCustom();
  • userCustom.setSex("1");
  • userCustom.setUsername("小");
  • userQueryVo.setUserCustom(userCustom);
  • Integer count = userMapper.findUserCount(userQueryVo);
  • System.out.println(count);
  • } catch (Exception e) {
  • e.printStackTrace();
  • } finally {
  • sqlSession.close();
  • }
  • }
  • @Test
  • public void testInsertUser() throws Exception {
  • SqlSession sqlSession = null;
  • try {
  • sqlSession = sqlSessionFactory.openSession();
  • // 创建UserMapper对象,mybatis自动生成mapper代理对象
  • UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  • User user = new User();
  • user.setUsername("hahak");
  • userMapper.insertUser(user);
  • sqlSession.commit();
  • } catch (Exception e) {
  • e.printStackTrace();
  • } finally {
  • sqlSession.close();
  • }
  • }
  • @Test
  • public void testDeleteUser() throws Exception {
  • SqlSession sqlSession = null;
  • try {
  • sqlSession = sqlSessionFactory.openSession();
  • // 创建UserMapper对象,mybatis 自动生成mapper代理对象
  • UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  • userMapper.deleteUser(36);
  • sqlSession.commit();
  • } catch (Exception e) {
  • e.printStackTrace();
  • } finally {
  • sqlSession.close();
  • }
  • }
  • }


  • 重要测试代码:

    	@Test
    	public void testFindUserList() throws Exception {
    		SqlSession sqlSession = null;
    		try {
    			sqlSession = sqlSessionFactory.openSession();
    			// 创建UserMapper对象,mybatis 自动生成mapper代理对象
    			UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
    			UserQueryVo userQueryVo = new UserQueryVo();
    			UserCustom userCustom = new UserCustom();
    			// userCustom.setSex("1");
    			// userCustom.setUsername("小");
    			userQueryVo.setUserCustom(userCustom);
    
    			// userQueryVo
    			List<UserCustom> list = userMapper.findUserList(null);
    			System.out.println(list);
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			sqlSession.close();
    		}
    	}
    
    	@Test
    	public void testFindUserCount() throws Exception {
    		SqlSession sqlSession = null;
    		try {
    			sqlSession = sqlSessionFactory.openSession();
    			// 创建UserMapper对象,mybatis 自动生成mapper代理对象
    			UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
    			UserQueryVo userQueryVo = new UserQueryVo();
    			UserCustom userCustom = new UserCustom();
    			userCustom.setSex("1");
    			userCustom.setUsername("小");
    			userQueryVo.setUserCustom(userCustom);
    
    			Integer count = userMapper.findUserCount(userQueryVo);
    			System.out.println(count);
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			sqlSession.close();
    		}
    	}



    单元测试结果

    对   List<UserCustom> findUserList(UserQueryVo userQueryVo) throws Exception; 进行的测试






    MyBatis - 常用标签与动态Sql

    MyBatis - 常用标签与动态Sql

    MyBatis常用标签

    ● 定义sql语句:select、insert、delete、update
    ● 配置JAVA对象属性与查询结构及中列明对应的关系:resultMap
    ● 控制动态sql拼接:if、foreach、choose
    ● 格式化输出:where、set、trim
    ● 配置关联关系:collection、association
    ● 定义常量及引用:sql、include

    MyBatis提供了对SQL语句动态的组装能力,大量的判断都可以在 MyBatis的映射XML文件里面配置,以达到许多我们需要大量代码才能实现的功能,大大减少了我们编写代码的工作量。

    动态SQL的元素

    元素 作用 备注 if 判断语句 单条件分支判断 choose、when、otherwise 相当于 Java 中的 switch case default 语句 多条件分支判断 trim、where、set 辅助元素 用于处理一些 SQL 拼装问题 foreach 循环语句 在in语句等列举条件常用

    一、定义sql标签

    ● select标签

     - id:唯一的标识符

     - parameterType:传给此语句的参数的全路径名或别名 例:com.sikiedu.beans.User 或 user

     - resultType:语句返回值类型或别名。注意,如果是集合,那么这里填写的是集合的泛型,而不是集合本身(resultType 与 resultMap 不能并用)

    <select id="selectUserById" parameterType="User" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>

    ● insert标签

     - id:唯一的标识符

     - parameterType:传给此语句的参数的全路径名或别名 例:com.sikiedu.beans.User

    <insert id="insertUser" parameterType="com.sikiedu.beans.User">
        INSERT INTO user VALUES(null,#{username},#{userpassword},#{balance},#{grgisterdate})
    </insert>

    ● delete标签

    - 属性同 insert

    <delete id="deleteUserById" parameterType="Integer">
        DELETE FROM user WHERE id = #{id}
    </delete>

    ● update标签

    - 属性同 insert

    <update id="updateUser" parameterType="com.sikiedu.beans.User">
        UPDATE user SET username = #{username} WHERE id = #{id}
    </update>

    二、动态 sql 拼接

    ● if标签 - if 标签通常用于 WHERE 语句、UPDATE 语句、INSERT 语句中。

    通过判断参数值来决定是否使用某个查询条件、判断是否更新某一个字段、判断是否插入某个字段的值。

    <select id="selectRoleListByRole" parameterType="Role" resultMap="roleResultMap">
        SELECT * FROM user
        <where>
            <if test="level!=null and level!=''''">
                level = #{level}
            </if>
            <if test="roletype!=null and roletype!=''''">
                AND roletype = #{roletype}
            </if>
            <if test="userid!=null">
                AND userid = #{userid}
            </if>
        </where>
    </select>

    ● foreach标签 -主要用于构建 in 条件,可在 sql 中对集合进行迭代。也常用到批量删除、添加等操作中。

     - collection:collection 属性的值有三个分别是 list、array、map 三种,分别对应的参数类型为:List、数组、map 集合。

     - item:表示在迭代过程中每一个元素的别名

     - index:表示在迭代过程中每次迭代到的位置(下标)

     - open:前缀

     - close:后缀

     - separator:分隔符,表示迭代时每个元素之间以什么分隔

    <select id="selectRoleListByids" resultMap="roleResultMap">
        SELECT * FROM user
        WHERE idrole IN
        <foreach collection="array" item="id" open="(" separator="," close=")">
            ${id}
        </foreach>
    </select>

    ● choose标签 - 类似于 Java 的 switch 语句

    有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。MyBatis 提供了 choose 元素,按顺序判断 when 中的条件出否成立,如果有一个成立,则 choose 结束。

    当 choose 中所有 when的条件都不满则时,则执行 otherwise 中的 sql。类似于 Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default

    <select id="getRoleListChoose" parameterType="Role" resultMap="roleResultMap">
        SELECT * FROM role
        <where>
            <choose>
                <when test="name!=null and name!='''' ">
                    AND name LIKE "%"#{name}"%"
                </when>
                <when test="userid!= null">
                    AND userid = #{userid}
                </when>
                <otherwise>
                    AND roletype = "射手"
                </otherwise>
            </choose>
        </where>
    </select>

    三、格式化输出

    ● where标签 - 解决if标签拼接字符串AND符号问题

    当 if 标签较多时,这样的组合可能会导致错误。 如下:

    <select id="getRoleListWhere" parameterType="Role" resultMap="roleResultMap">
        SELECT * FROM role WHERE
        <if test="name!=null and name!='' '' ">
            name = #{name}
        </if>
        <if test="roletype!=null and roletype!='' '' ">
            AND roletype = #{roletype}
        </if>
        <if test="userid!=null">
            AND userid = #{userid}
        </if>
    </select>

    当 name 值为 null 时,查询语句会出现 “WHERE AND” 的情况,解决该情况除了将"WHERE"改为“WHERE 1=1”之外,还可以利用 where标签。

    这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。

    <select id="selectRoleListByRole" parameterType="Role" resultMap="roleResultMap">
        <include refid="myselect"></include>
        <where>
            <if test="name!=null and name!=''''">
                name = #{name}
            </if>
            <if test="roletype!=null and roletype!=''''">
                AND roletype = #{roletype}
            </if>
            <if test="userid!=null">
                AND userid = #{userid}
            </if>
        </where>
    </select>

    ● set标签 - 解决更新数据表时字符串拼接逗号”,”问题;

    没有使用 if 标签时,如果有一个参数为 null,都会导致错误。

    当在 update 语句中使用 if 标签时,如果最后的 if 没有执行,则或导致逗号多余错误。

    使用 set 标签可以将动态的配置 set 关键字,和剔除追加到条件末尾的任何不相关的逗号。

    <update id="updateSetRole" parameterType="Role">
        UPDATE role
        SET name = ''${name}'',
        level = ${level},
        roletype = ''${roletype}'',
        userid = ${userid}
        WHERE idrole = #{id}
    </update>
    
    <update id="updateSetRole" parameterType="Role">
        UPDATE role SET
        <if test="name != null and name != ''''">
            name = ''${name}'',
        </if>
        <if test="level != null">
            level = ${level},
        </if>
        <if test="roletype != null and roletype != ''''">
            roletype = ''${roletype}'',
        </if>
        <if test="userid != null">
            userid = ${userid}
        </if>
        WHERE idrole = #{id}
    </update>

    使用 set+if 标签修改后,如果某项为 null 则不进行更新,而是保持数据库原值。

    <update id="updateSetRole" parameterType="Role">
        UPDATE role
        <set>
            <if test="name != null and name != ''''">
                name = ''${name}'',
            </if>
            <if test="level != null">
                level = ${level},
            </if>
            <if test="roletype != null and roletype != ''''">
                roletype = ''${roletype}'',
            </if>
            <if test="userid != null">
                userid = ${userid}
            </if>
        </set>
        WHERE idrole = #{id}
    </update>

    ● trim标签 - trim标记是一个格式化的标记,主要用于拼接sql的条件语句(前缀或后缀的添加或忽略),可以完成set或者是where标记的功能

     - prefix:在trim标签内sql语句加上前缀

     - suffix:在trim标签内sql语句加上后缀

     - prefixOverrides:指定去除多余的前缀内容,如:prefixOverrides=“AND | OR”,去除trim标签内sql语句多余的前缀"and"或者"or"。

     - suffixOverrides:指定去除多余的后缀内容。

    <select id="selectRoleListByTrim" parameterType="Role" resultMap="roleResultMap">
        SELECT * FROM role
        <trim prefix="WHERE" suffixOverrides="AND">
            <if test="name!=null and name!=''''">
                name = #{name} AND
            </if>
            <if test="roletype!=null and roletype!=''''">
                roletype = #{roletype} AND
            </if>
            <if test="userid!=null">
                userid = #{userid} AND
            </if>
        </trim>
    </select>

    如果name、roletype和userid的值都不为空的话,会执行如下语句

    SELECT * FROM role WHERE name = #{name} AND roletype = #{roletype} AND userid = #{userid}

    会为片段添加 “WHERE” 前缀,并忽略最后个 “and” 

    四、定义常量及引用

    ● sql标签 – 可以提取重复sql语句片段;

    当多种类型的查询语句的查询字段或者查询条件相同时,可以将其定义为常量,方便调用。为求 <select> 结构清晰也可将 sql 语句分解。

    <sql id="myselect">
        SELECT * FROM role
    </sql>

    ● include - 用于引用定义的常量

    <resultMap type="Role" id="roleRM">
        <id property="id" column="idrole" />
    </resultMap>
    <select id="selectAllRole" resultMap="roleRM">
        <include refid="myselect"></include>
    </select>
    
    <select id="selectRoleListByids" resultMap="roleRM">
        <include refid="myselect"></include>
        WHERE idrole IN
        <foreach collection="array" item="id" open="(" separator="," close=")">
            ${id}
        </foreach>
    </select>

    我们今天的关于Mybatis的缓存与动态SQLmybatis怎么保持缓存和数据库一致性的分享就到这里,谢谢您的阅读,如果想了解更多关于day64(MYBATIS框架基础2:查询数据,动态SQL ,关联查询,小结,关于#{}和${}格式的占位符,Mybatis的缓存机制)、java web(七): mybatis的动态sql和mybatis generator自动生成pojo类和映射文件、JavaEE_Mybatis_SpringMVC__Mybatis_lesson8_Mybatis的动态sql、MyBatis - 常用标签与动态Sql的相关信息,可以在本站进行搜索。

    本文标签: