【超全必看】Mybatis基础入门学习总结笔记(附搭建过程及示例代码)【下】

Mybatis映射文件的SQL深入

if标签

初始配置
SqlMapConfig.xml

?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!-- 配置 -->
<configuration>
    <properties resource="jdbc.properties">
    </properties>
    
    <typeAliases>
        <package name="domain"/>
    </typeAliases>
    <!-- 环境配置 -->
    <environments default="mysql">
        <!-- id和上面的值相同 -->
        <environment id="mysql">
            <!-- 事务类型为jdbc -->
            <transactionManager type="JDBC"/>
            <!-- 数据源配置 -->
            <dataSource type="POOLED">
                <!-- 驱动 url 用户名 密码 -->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 配置映射 -->
    <mappers>
        <package name="dao"/>
    </mappers>
</configuration>

IUserDao.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="dao.IUserDao">
    <resultMap type="domain.User" id="userMap">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="sex" property="sex"/>
        <result column="address" property="address"/>
        <result column="birthday" property="birthday"/>
    </resultMap>

    <select id="findAll" resultMap="userMap">
        select * from user;
    </select>


    <select id="findById" parameterType="INT" resultMap="userMap">
        select * from user where id=#{uid};
    </select>

    <select id="findLike" parameterType="string" resultMap="userMap">
            select * from user where username like #{likename};
    </select>


    <select id="findByVo" parameterType="QueryVo" resultMap="userMap">
            select * from user where username like #{user.username};
    </select>
</mapper>

在IUserDao新建方法,根据条件进行查询

//根据传入参数条件查询
    List<User> findUserByCondition(User user);

修改映射配置
根据传入的姓名和性别进行查询

	<select id="findUserByCondition" parameterType="user" resultMap="userMap">
        select * from user where 1=1
        <if test="username!=null">
           and  username = #{username}
        </if>
        <if test="sex!=null">
            and sex= #{sex}
        </if>
    </select>

先在测试方法中只加入姓名进行查询

 @Test
    //条件查询
    public void findbyCondition(){
        //调用方法
        User user = new User();
        user.setUsername("范闲");
        List<User> users = userDao.findUserByCondition(user);
        users.forEach(System.out::println);
    }

结果:

在这里插入图片描述
再对性别进行设置

	@Test
    //条件查询
    public void findbyCondition(){
        //调用方法
        User user = new User();
        user.setUsername("范闲");
        user.setSex("女");
        List<User> users = userDao.findUserByCondition(user);
        users.forEach(System.out::println);
    }

进行查询,结果仅剩一条

在这里插入图片描述


where标签

去掉where=1,把if标签放入where即可

	<select id="findUserByCondition" parameterType="user" resultMap="userMap">
        select * from user 
        <where>
            <if test="username!=null">
                and  username = #{username}
            </if>
            <if test="sex!=null">
                and sex= #{sex}
            </if>
        </where>
    </select>

foreach标签

实现一个按id集合查询的案例
在先前的QueryVo中新增一个ids属性

public class QueryVo {
    private User user;

    private List<Integer> ids;

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}


添加dao接口方法

	//根据queryVo中的id集合查询
    List<User> findUserInIds(QueryVo queryVo);

更改映射配置

  • <foreach>标签用于遍历集合,它的属性:
  • collection:代表要遍历的集合元素,注意编写时不要写#{}
  • open:代表语句的开始部分
  • close:代表结束部分
 <select id="findUserInIds" parameterType="QueryVo" resultMap="userMap">
        select * from user
        <where>
            <if test="ids!=null and ids.size()>0">
                <foreach collection="ids" open="id in (" close=")" item="id" separator=",">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>

测试

 @Test
    //id集合查询用户
    public void findIn(){
        QueryVo queryVo=new QueryVo();
        List<Integer> list=new ArrayList<>();
        list.add(53);
        list.add(57);
        queryVo.setIds(list);
        //调用方法
        List<User> users = userDao.findUserInIds(queryVo);
        users.forEach(System.out::println);
    }

结果

在这里插入图片描述


抽取重复sql语句

定义代码片段

<!-- 抽取重复的语句代码片段 --> 
<sql id="queryAll">
select * from user
</sql>

印用代码片段
在这里插入图片描述


Mybatis的多表关联查询

数据库的准备

之前已经建立了一个user表,再创建一个account表

DROP TABLE IF EXISTS `account`;

CREATE TABLE `account` (
  `ID` int(11) NOT NULL COMMENT '编号',
  `UID` int(11) default NULL COMMENT '用户编号',
  `MONEY` double default NULL COMMENT '金额',
  PRIMARY KEY  (`ID`),
  KEY `FK_Reference_8` (`UID`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `account`(`ID`,`UID`,`MONEY`) values (1,46,1000),(2,45,1000),(3,46,2000);

项目准备

IUserDao接口

public interface IUserDao {
    //查询所有用户
    List<User> findAll();

    //查询一个用户
    User findById(Integer id);

}

IUserDao.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="dao.IUserDao">

    <select id="findAll" resultType="USER">
         select * from user
    </select>


    <select id="findById" parameterType="INT" resultType="USER">
         select * from user where id=#{uid};
    </select>

</mapper>

Account实体类

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }
}


IAccountDao接口

public interface IAccountDao {

    List<Account> findAll();
}


IAccountDao.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="dao.IAccountDao">

    <select id="findAll" resultType="account">
         select * from account
    </select>

</mapper>

测试类

public class MybatisTest {
    private InputStream inputStream;
    private SqlSession sqlSession;
    private IUserDao userDao;
    private IAccountDao accountDao;

    @Before//在Test执行前执行
    public void init() throws IOException {
        //读取配置文件
        inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建sqlsession工厂对象
        SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);
        //创建sqlsession对象
        sqlSession = factory.openSession(true);
        //创建IUserDao代理类
        userDao = sqlSession.getMapper(IUserDao.class);
        accountDao=sqlSession.getMapper(IAccountDao.class);
    }

    @After
    public void destroy() throws IOException {
        //关闭资源
        inputStream.close();
    }

    @Test
    //查询所有用户
    public void test(){
        //调用方法
        List<User> users = userDao.findAll();
        //打印结果
        users.forEach(System.out::println);
    }


    @Test
    //查询一个用户
    public void findbyid(){
        //调用方法
        User user = userDao.findById(48);
        System.out.println(user);
    }

    @Test
    //查询账户
    public void findAccount(){
        List<Account> accounts = accountDao.findAll();
        accounts.forEach(System.out::println);
    }
}

执行findAccount方法,说明对account表的配置成功了
在这里插入图片描述


通过编写Account子类实现多表一对一查询

需求
查询所有账户信息,关联查询下单用户信息。
注意:
因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户。


AccountUser类

public class AccountUser extends Account {
    private String username;
    private String address;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return super.toString()+"   User{" +
                "username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}


dao接口方法

//查询用户及账户
    List<AccountUser> findUserAndAccount();

映射配置

  <select id="findUserAndAccount" resultType="AccountUser">
         select a.*,u.username,u.address from account a,user u where a.uid=u.id
    </select>

测试方法

 @Test
    //查询用户和账户
    public void findUserAndAccount(){
        List<AccountUser> uaa = accountDao.findUserAndAccount();
        uaa.forEach(System.out::println);
    }

结果
在这里插入图片描述


通过建立实体类关系实现多表一对一查询

在Account类中添加user属性

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;   
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }
}


配置映射
association指定类中引用的类属性:

 <resultMap id="map" type="account">
        <id property="id" column="aid"/>
        <result property="uid" column="uid"/>
        <result property="money" column="money"/>
        <association property="user" column="uid" javaType="User">
            <id property="id" column="id"/>
            <result property="username" column="username"/>
            <result property="address" column="address"/>
            <result property="sex" column="sex"/>
        </association>
    </resultMap>

    <select id="findUserAndAccount" resultMap="map">
         select u.*,a.id as aid,a.uid,a.money from account a,user u where a.uid=u.id
    </select>

测试方法

@Test
    //查询用户和账户
    public void findUserAndAccount(){
        List<Account> accounts = accountDao.findUserAndAccount();
        for(Account account:accounts){
            System.out.println(account);
            System.out.println(account.getUser());
            System.out.println("----------------");
        }
    }

结果:
在这里插入图片描述


多表一对多的查询

需求:
查询所有用户信息及用户关联的账户信息。
分析:
用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适。


实体类User

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    //一对多关系映射,主表实体包含从表实体的集合引用
    private List<Account> accounts;

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}


IUserDao.xml映射配置

 <resultMap id="map" type="User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
        <!-- 配置user对象中accounts集合的映射 -->
        <collection property="accounts" ofType="Account">
            <id property="id" column="aid"/>
            <result property="uid" column="uid"/>
            <result property="money" column="money"/>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="map">
         select u.*,a.id as aid,a.uid,a.money from user u left join account a on u.id=a.uid
    </select>

collection
部分定义了用户关联的账户信息。表示关联查询结果集
property=“accounts”:
关联查询的结果集存储在 User 对象的上哪个属性。
ofType=“account”:
指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名


测试方法

@Test
    //查询所有用户
    public void test(){
        //调用方法
        List<User> users = userDao.findAll();
        //打印结果
        for(User user:users){
            System.out.println(user);
            System.out.println(user.getAccounts());
            System.out.println("----------------");
        }
    }

结果
在这里插入图片描述


多表多对多的查询

需求:
实现查询所有对象并且加载它所分配的用户信息。
分析:
查询角色我们需要用到Role表,但角色分配的用户的信息我们并不能直接找到用户信息,而是要通过中间表(USER_ROLE 表)才能关联到用户信息。


数据库role表和user_role中间表的准备

DROP TABLE IF EXISTS `role`;

CREATE TABLE `role` (
  `ID` int(11) NOT NULL COMMENT '编号',
  `ROLE_NAME` varchar(30) default NULL COMMENT '角色名称',
  `ROLE_DESC` varchar(60) default NULL COMMENT '角色描述',
  PRIMARY KEY  (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) values (1,'院长','管理整个学院'),(2,'总裁','管理整个公司'),(3,'校长','管理整个学校');

DROP TABLE IF EXISTS `user_role`;

CREATE TABLE `user_role` (
  `UID` int(11) NOT NULL COMMENT '用户编号',
  `RID` int(11) NOT NULL COMMENT '角色编号',
  PRIMARY KEY  (`UID`,`RID`),
  KEY `FK_Reference_10` (`RID`),
  CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
  CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `user_role`(`UID`,`RID`) values (41,1),(45,1),(41,2);

实体类Role

public class Role implements Serializable {
    private Integer id;
    private String roleName;
    private String roleDesc;

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", roleName='" + roleName + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }
}


IRoleDao接口

public interface IRoleDao {
  
    //查询所有
    List<Role> findAll();    
}


IRoleDao.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="dao.IRoleDao">

    <resultMap id="map" type="Role">
        <id property="id" column="id"/>
        <result property="roleName" column="role_name"/>
        <result property="roleDesc" column="role_desc"/>
    </resultMap>

    <select id="findAll" resultMap="map">
        select * from role;
    </select>

</mapper>

先测试能否成功查询role表

public class MybatisTest {
    private InputStream inputStream;
    private SqlSession sqlSession;
    private IUserDao userDao;
    private IRoleDao roleDao;

    @Before//在Test执行前执行
    public void init() throws IOException {
        //读取配置文件
        inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建sqlsession工厂对象
        SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);
        //创建sqlsession对象
        sqlSession = factory.openSession(true);
        //创建IUserDao代理类
        userDao = sqlSession.getMapper(IUserDao.class);
        roleDao=sqlSession.getMapper(IRoleDao.class);
    }

    @After
    public void destroy() throws IOException {
        //关闭资源
        inputStream.close();
    }
    @Test
    //查询所有用户
    public void test1(){
        //调用方法
        List<Role> roles = roleDao.findAll();
        //打印结果
        for(Role role:roles){
            System.out.println(role);
        }
    }
}

结果

在这里插入图片描述


实现role表对user表的多对多查询
在role实体类添加users属性

 private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

配置映射

<?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="dao.IRoleDao">

    <resultMap id="map" type="Role">
        <id property="roleId" column="rid"/>
        <result property="roleName" column="role_name"/>
        <result property="roleDesc" column="role_desc"/>
        <collection property="users" ofType="User">
            <id property="id" column="id"/>
            <result property="username" column="username"/>
            <result property="sex" column="sex"/>
            <result property="address" column="address"/>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="map">
        select u.*,r.id as rid,r.role_name,r.role_desc from role r
        left join user_role ur on r.id=ur.rid
        left join user u on u.id=ur.uid
    </select>


</mapper>

查询及结果

@Test
    //查询所有用户
    public void test1(){
        //调用方法
        List<Role> roles = roleDao.findAll();
        //打印结果
        for(Role role:roles){
            System.out.println(role);
            System.out.println("对应用户信息");
            System.out.println(role.getUsers());
            System.out.println("-----------");
        }
    }

在这里插入图片描述


实现user表对role的多对多查询
给User类添加以下属性

private List<Role> roles;

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

配置IUserDao.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="dao.IUserDao">

    <resultMap id="map" type="User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
        <collection property="roles" ofType="Role">
            <id property="roleId" column="rid"/>
            <result property="roleName" column="role_name"/>
            <result property="roleDesc" column="role_desc"/>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="map">
        select u.*,r.id as rid,r.role_name,r.role_desc from user u
        left join user_role ur on u.id=ur.uid
        left join role r on r.id=ur.rid
    </select>

    

</mapper>

测试及结果

@Test
    //查询所有用户
    public void test(){
        //调用方法
        List<User> users = userDao.findAll();
        //打印结果
        for(User user:users){
            System.out.println(user);
            System.out.println("----------------");
            System.out.println(user.getRoles());
        }
    }

在这里插入图片描述


Mybatis的延迟加载

实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的账户信息。此时就是我们所说的延迟加载。
延迟加载:
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
坏处:
因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降


实现一对一的延迟加载
User类和Account类:和之前多表实现一对一查询时相同
配置文件:
由于延迟加载,所以更改了sql语句,只对account表进行查询,所以无法获得user表的信息,所以在select属性中表明调用IUserDao的findbyid方法,其中id的值就是column中的uid,也就是对account表进行查询,然后在查询结果中利用uid和IUserDao的findbyid方法再查询user信息。

IAccountDao.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="dao.IAccountDao">

    <!-- 定义封装account和user的resultMap -->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 一对一的关系映射:配置封装user的内容
          select属性指定的内容:查询用户的唯一标识
          column属性指定的内容:用户根据id查询时,所需要的参数的值
        -->
       <association property="user" javaType="user" select="dao.IUserDao.findById" column="uid"/>

    </resultMap>

    <!-- 查询所有 -->
    <select id="findAll" resultMap="accountUserMap">
        select *  from account
    </select>


</mapper>

IUserDao.xml配置:
本例只用标明此配置即可

	<select id="findById" parameterType="INT" resultType="USER">
         select * from user where id=#{uid};
    </select>

调用方法测试:

@Test
    //查询账户
    public void testAccount(){
        //调用方法
        List<Account> accounts = accountDao.findAll();
        //打印结果
        for(Account account:accounts){
            System.out.println(account);
            System.out.println("----------------");
            System.out.println(account.getUser());
        }
    }

结果:
可以看到并没有延迟加载 第一条查询语句的Parameters中有两条sql语句
在这里插入图片描述


在SqlMapConfig主配置中开启延迟加载

 <!-- 开启延迟加载 -->
    <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

再次测试:
可以看到延迟加载成功,在输出了结果后仍有一次加载

在这里插入图片描述


一对多的延迟加载

在User类中添加accounts属性

private List<Account> accounts;

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

在IAccountDao接口中添加根据uid查询account记录的方法

	//根据uid查询account
    Account findAccountByUid(Integer uid);

IAccountDao.xml中对根据uid查询记录进行配置

<select id="findAccountByUid" parameterType="INT" resultType="Account">
         select * from account where uid = #{uid}
    </select>

IUserDao.xml中的配置
跟一对一延迟加载时类似

<resultMap type="user" id="userMap">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="address" property="address"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
        <!-- collection 是用于建立一对多中集合属性的对应关系
        ofType 用于指定集合元素的数据类型
        select 是用于指定查询账户的唯一标识(账户的 dao 全限定类名加上方法名称)
        column 是用于指定使用哪个字段的值作为条件查询
        -->
        <collection property="accounts" ofType="account" select="dao.IAccountDao.findAccountByUid" column="id"/>
    </resultMap>
    
    <!-- 配置查询所有操作 --> 
    <select id="findAll" resultMap="userMap">
        select * from user
    </select>

关闭延迟加载时,仅调用方法而不作输出

 @Test
    //查询所有用户
    public void test(){
        //调用方法
        List<User> users = userDao.findAll();
    }

依然执行了所有sql语句

在这里插入图片描述

开启延迟加载后,仅仅执行一句
在这里插入图片描述


Mybatis的缓存

Mybatis的缓存:

  • 概念
    存在于内存中的临时数据
  • 使用的目的
    减少和数据库的交互次数,提高执行效率
  • 适用于缓存的数据
    经常查询且不常改变的,数据的正确性对最终结果影响不大。

一级缓存

指的是Mybatis中SqlSession对象的缓存,当我们执行完查询结果后,查询的结果会同时存入到SqlSession为我们提供的一块区域中,该区域的结构是一个Map,当我们再次查询同样数据时,Mybatis会先去SqlSession中查询是否有,有的话直接拿出来用。
当SqlSession对象消失时,Mybatis的一级缓存也就消失了。

测试一级缓存
将User类中的toString方法注释

public void findbyid(){
        //调用方法
        User user1 = userDao.findById(48);
        System.out.println(user1);
        User user2 = userDao.findById(48);
        System.out.println(user2);
        System.out.println(user1==user2);
    }

运行结果,可以发现相同查询获取的结果是同一个
在这里插入图片描述


如果清空缓存再测试

public void findbyid(){
        //调用方法
        User user1 = userDao.findById(48);
        System.out.println(user1);
        sqlSession.clearCache();
        User user2 = userDao.findById(48);
        System.out.println(user2);
        System.out.println(user1==user2);
    }

结果不是同一个

在这里插入图片描述
一级缓存的清除
一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。


二级缓存

二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

未开启二级缓存时的测试

public class CacheTest {
    private InputStream inputStream;
    SqlSessionFactory factory;

    @Before//在Test执行前执行
    public void init() throws IOException {
        //读取配置文件
        inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建sqlsession工厂对象
        factory=new SqlSessionFactoryBuilder().build(inputStream);

    }

    @Test
    public void test(){
        SqlSession sqlSession1 = factory.openSession();
        IUserDao userDao1 = sqlSession1.getMapper(IUserDao.class);
        User u1 = userDao1.findById(48);
        System.out.println(u1);
        sqlSession1.close();//清空缓存
        SqlSession sqlSession2 = factory.openSession();
        IUserDao userDao2 = sqlSession2.getMapper(IUserDao.class);
        User u2 = userDao2.findById(48);
        System.out.println(u2);
        System.out.println(u1==u2);

    }

}

结果:
查询了两次sql语句,并且User对象不是同一个
在这里插入图片描述


开启二级缓存
第一步:在 SqlMapConfig.xml 文件开启二级缓存

<settings>
<!-- 开启二级缓存的支持 --> <setting name="cacheEnabled" value="true"/>
</settings>

因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为false 代表不开启二级缓存。


第二步:配置相关的 Mapper 映射文件
在IUserdao.xml中:
<cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。

<!-- 开启二级缓存的支持 -->
    <cache/>

第三步:配置 statement 上面的 useCache 属性

<!-- 根据 id 查询 --> <select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select> 

将 UserDao.xml 映射文件中的标签中设置 useCache=”true”代表当前这个 statement 要使用
二级缓存,如果不使用二级缓存可以设置为 false。
注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。

可以看到只进行了一次sql语句的查询,但是对象仍然不是同一个,这是因为二级缓存存储的是数据,而不是对象。
在这里插入图片描述


Mybatis的注解开发

入门案例

环境配置不再过多叙述,新建maven工程,导入依赖,具体配置见上篇文章开头
创建一个实体类User

public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

IUserDao接口

public interface IUserDao {
    
    @Select("select * from user")
    List<User> findAll();
}


引入jdbc.peoperties,根据自己情况调整

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///test?serverTimezone=UTC
username=root
password=sjh2019

主配置文件SqlMapConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!-- 配置 -->
<configuration>
    <!-- 引入外部配置 -->
    <properties resource="jdbc.properties"/>
    <!-- 配置别名(实体类位置) -->
    <typeAliases>
        <package name="domain"/>
    </typeAliases>
    <!-- 环境配置 -->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- 驱动 url 用户名 密码 -->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>


    <mappers>
        <!-- 接口所在位置 -->
        <package name="dao"/>
    </mappers>
</configuration>

测试类

public class AnnoTest {


    public static void main(String[] args) throws Exception{
        //1.获取字节输入流
        InputStream in= Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.根据输入流构建sqlsessionfactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.生产sqlsession
        SqlSession sqlSession = factory.openSession();
        //4.获取dao代理对象
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);
        //5.执行方法
        List<User> users = userDao.findAll();
        users.forEach(System.out::println);
        //6.释放资源
        sqlSession.close();
        in.close();
    }
}


结果
在这里插入图片描述


利用注解实现其他CRUD操作
由于测试代码大同小异,这里只放出IUserDao中的注解配置

	//插入
    @Insert("insert into user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address})")
    void insert(User user);

    //更新
    @Update("update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id = #{id}")
    void update(User user);

    //删除
    @Delete("delete from user where id = #{id}")
    void delete(Integer id);

    //查询一个
    @Select("select * from user where id = #{id}")
    User findById(Integer id);

    //根据名称模糊查询
    @Select("select * from user where username = #{username}")//测试时手动加入%符号
    List<User> findByName(String username);
    
    //查询记录总数
    @Select("select count(*) from user")
    int count();

利用注解解决实体类属性和数据库列名不一致问题

User类改为

public class User implements Serializable {
    private Integer userId;
    private String userName;
    private Date useBirthday;
    private String userSex;
    private String userAddress;

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Date getUseBirthday() {
        return useBirthday;
    }

    public void setUseBirthday(Date useBirthday) {
        this.useBirthday = useBirthday;
    }

    public String getUserSex() {
        return userSex;
    }

    public void setUserSex(String userSex) {
        this.userSex = userSex;
    }

    public String getUserAddress() {
        return userAddress;
    }

    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", useBirthday=" + useBirthday +
                ", userSex='" + userSex + '\'' +
                ", userAddress='" + userAddress + '\'' +
                '}';
    }
}


注解配置
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装

 	@Select("select * from user")
    @Results(id="userMap",
            value= {
                    @Result(id=true,column="id",property="userId"),//id为true表示主键
                    @Result(column="username",property="userName"),
                    @Result(column="sex",property="userSex"),
                    @Result(column="address",property="userAddress"),
                    @Result(column="birthday",property="userBirthday")
            })
    List<User> findAll();

注解实现一对一(多对一)查询

在domain包下添加一个Account类

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
    private User user;

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                ", user=" + user +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}


在dao包下添加IAccountDao接口

public interface IAccountDao {

    @Select("select * from account")
    @Results(id="accountMap",
            value= {
                    @Result(id=true,column="id",property="id"),
                    @Result(column="uid",property="uid"),
                    @Result(column="money",property="money"),
                    @Result(column="uid", property="user",
                            one=@One(select="dao.IUserDao.findById",
                                    fetchType= FetchType.LAZY) )
               //one表示对应关系 select是引用的方法 fetchtype是加载策略
            })
    List<Account> findAll();
}

对应的IUserDao中的配置

	//查询一个
    @Select("select * from user where id = #{id}")
    @ResultMap("userMap")
    User findById(Integer id);

注解实现一对多查询

User类加入accounts属性

//一对多关系映射:主表方法应该包含一个从表方的集合引用
    private List<Account> accounts;

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

IAccountDao加入根据uid查询的方法

@Select("select * from account where uid = #{uid} ")
	//这里不加resultmap是因为实体类属性和数据库表列名一致
    List<Account> findByUid(Integer userId);

IUserDao配置

 @Select("select * from user")
    @Results(id="userMap",
            value= {
                    @Result(id=true,column="id",property="userId"),
                    @Result(column="username",property="userName"),
                    @Result(column="sex",property="userSex"),
                    @Result(column="address",property="userAddress"),
                    @Result(column="birthday",property="userBirthday"),
                    @Result(column="id",property = "accounts",many=@Many(
                            select="dao.IAccountDao.findByUid",
                            fetchType= FetchType.LAZY
                    ) )
            })
    List<User> findAll();

配置二级缓存

第一步:在 SqlMapConfig.xml 文件开启二级缓存

<settings>
<!-- 开启二级缓存的支持 --> 
<setting name="cacheEnabled" value="true"/>
</settings>

第二步:在dao接口上加上注解

@CacheNamespace(blocking=true)//mybatis 基于注解方式实现配置二级缓存
发布了66 篇原创文章 · 获赞 302 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_41112238/article/details/103837489