Mybatis框架(二)

一、MyBatis的连接池

1. 连接池技术

  1. 连接池是什么:存储连接的容器,主要用于管理数据库连接。数据库连接是数据库操作的基础,但建立和关闭连接通常需要消耗大量的时间。为了提高性能,许多应用程序使用连接池来重用已建立的数据库连接,而不是为每个新的请求创建一个新的连接。
  2. 解决了什么问题:如果没有连接池,那么每次都执行SQL语句都会创建connection连接,会浪费时间。影响程序的性能。

2. MyBatis连接池的分类

  1. MyBatis内置了连接池技术,dataSource标签的type属性有3个取值
    1. POOLED 使用连接池
    2. UNPOOLED 不使用连接池
    3. JNDI 使用JNDI实现连接池

二、MyBatis映射文件SQL深入

1. 动态SQL语句之if标签

  1. 之前做过拼接SQL语句查询条件的查询,需要动态的拼接SQL语句
  2. UserMapper接口的方法
package com.qcby.mapper;
import java.util.List;
import com.qcby.domain.User;
​
public interface UserMapper {
    
    // 条件查询
    public List findByWhere(User user);
    
}

        3.UserMapper.xml配置文件

    

        4.测试方法

 /**
     * 条件查询
     * @throws IOException
     */
    @Test
    public void testFindByWhere() throws IOException {
        // 先加载主配置文件,加载到输入流中
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 创建SqlSessionFactory对象,创建SqlSession对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        // 创建SqlSession对象
        SqlSession session = factory.openSession();
​
        User user = new User();
        user.setUsername("%熊%");
        user.setSex("女");
        
        // 条件查询
        UserMapper mapper = session.getMapper(UserMapper.class);
        List list = mapper.findByWhere(user);
        for (User user1 : list) {
            System.out.println(user1);
        }
        
        // 关闭资源
        session.close();
        inputStream.close();
    }   ​

2. 动态SQL语句之where标签

  1. where标签目的就是为了去掉 where 1=1的拼接的
  2. where标签使用在if标签的外面
  3. 代码
 
    

3. 动态SQL语句之foreach标签

  1. 需求一
    1. 需求的SQL语句:select * from user where id = 1 or id = 2 or id = 3
    2. 在User类中添加属性
public class User implements Serializable{
    
    private static final long serialVersionUID = 525400707336671154L;
    
    // 主键
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    
    // 定义ids属性,用来存储所有的id
    private List ids;
    
    // 提供get和set方法

                3.在UserMapper接口中添加方法

public List findByIds(User user);

                4.编写测试方法

/**
     * foreach标签
     * @throws IOException
     */
    @Test
    public void testFindByIds() throws IOException {
        // 先加载主配置文件,加载到输入流中
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 创建SqlSessionFactory对象,创建SqlSession对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        // 创建SqlSession对象
        SqlSession session = factory.openSession();
​
        User user = new User();
        List ids = new ArrayList<>();
        ids.add(1);
        ids.add(2);
        ids.add(3);
        user.setIds(ids);
​
        // 条件查询
        UserMapper mapper = session.getMapper(UserMapper.class);
        List list = mapper.findByIds(user);
        for (User user1 : list) {
            System.out.println(user1);
        }
​
        // 关闭资源
        session.close();
        inputStream.close();
    }    ​

                5.编写配置文件

 
       

  2.需求二

  1. 需求SQL:select * from user where id in (1,2,3)
  2. 配置文件编写

      

第二种方法:不需要User类中添加List ids属性

a、在UserMapper接口中添加方法

public List findByIdsList(List list);

b、编写配置文件

 
    

c、编写测试代码

@Test
public void testFindByIdsList() throws IOException {
    // 先加载主配置文件,加载到输入流中
    InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    // 创建SqlSessionFactory对象,创建SqlSession对象
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
    // 创建SqlSession对象
    SqlSession session = factory.openSession();

    List ids = new ArrayList<>();
    ids.add(1);
    ids.add(2);
    ids.add(3);

    // 条件查询
    UserMapper mapper = session.getMapper(UserMapper.class);
    List list = mapper.findByIdsList(ids);
    for (User user1 : list) {
        System.out.println(user1);
    }

    // 关闭资源
    session.close();
    inputStream.close();
}

4. 提取公用的SQL语句

代码如下





        select * from user
    
​
    
​
    
    
​
    
​
    
    

三、一对多查询

1. 多表设计

  1. 一对一 其实一对一可以设计成一张表结构
  2. 一对多
  3. 多对一
  4. 多对多

2. 搭建开发的环境

  1. 执行建表语句
CREATE TABLE `user` (
  `id` int(11) NOT NULL auto_increment,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` datetime default NULL COMMENT '生日',
  `sex` char(1) default NULL COMMENT '性别',
  `address` varchar(256) default NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
​
insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'老王','2018-02-27 17:47:08','男','北京'),(2,'熊大','2018-03-02 15:09:37','女','上海'),(3,'熊二','2018-03-04 11:34:34','女','深圳'),(4,'光头强','2018-03-04 12:04:06','男','广州');
​
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,1,1000),(2,2,1000),(3,2,2000); ​

      2.编写Account的JavaBean类

package com.qcby.domain;
import java.io.Serializable;

/**
 * 账号
 */
public class Account implements Serializable{
    private Integer id;
    private Integer uid;
    private Double 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;
    }
}

      3.编写AccountMapper接口

package com.qcby.mapper;

public interface AccountMapper {
}​

      4.编写AccountMapper.xml的配置文件




     5.主配置文件中引入映射配置文件



    
    

3. 多对一查询(一对一查询)

  1. 需求:查询的数据中包含account所有的字段,再包含用户的名称和地址
  2. 在Account类中添加user的属性,表示该帐户只属于这个用户
// 用户
private User user;

    3.在AccountMapper接口中编写查询的方法

 public List findAll();

    4.编写配置文件AccountMapper.xml


    
        
        
        
        
            
            
        
    

     5.测试方法

/**
     * 多对一查询
     * @throws IOException
     */
    @Test
    public void testFindAll() throws IOException {
        // 先加载主配置文件,加载到输入流中
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 创建SqlSessionFactory对象,创建SqlSession对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        // 创建SqlSession对象
        SqlSession session = factory.openSession();
        // 获取代理对象
        AccountMapper mapper = session.getMapper(AccountMapper.class);
        // 查询
        List list = mapper.findAll();
        // 遍历
        for (Account account : list) {
            System.out.println(account);
        }
​
        // 关闭资源
        session.close();
        inputStream.close();
    }
​ ​

4. 一对多查询

  1. 如果想查询 select u.*,a.money from user u left join account a on u.id = a.uid 语句的内容
  2. 在User类中添加List的属性
package com.qcby.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;

/**
 * 实现类  推荐使用序列化接口 HttpSession序列化
 */
public class User implements Serializable{
    // 主键
    private Integer id;
    // 用户名
    private String username;
    // 生日
    private Date birthday;
    // 性别
    private String sex;
    // 地址
    private String address;

    // 演示foreach标签
    private List ids;

    // 演示一对多查询
    private List 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;
    }

    public List getIds() {
        return ids;
    }

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

    public List getAccounts() {
        return accounts;
    }

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

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

     3.在UserMapper接口中定义方法

    // 查询一对多
    public List findOneToMany();    

     4.编写配置文件UserMapper.xml





    
    
    
    
    
    
        
    
    

     5.编写测试的方法

/**
     * 一对多查询
     */
    @Test
    public void testOneToMany() throws IOException {
        // 先加载主配置文件,加载到输入流中
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 创建SqlSessionFactory对象,创建SqlSession对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        // 创建SqlSession对象
        SqlSession session = factory.openSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        List list = mapper.findOneToMany();
        // 遍历list集合
        for (User user : list) {
            System.out.println(user);
        }
        // 关闭资源
        session.close();
        inputStream.close();
    }  ​

四、多对多查询

1. 多对多查询

  1. 执行SQL语句,创建表结构
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,'校长','管理整个学校');
​
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 (1,1),(1,2),(2,2); 

     2.编写JavaBean

package com.qcby.domain;
import java.io.Serializable;
import java.util.List;

/**
 * 角色模块
 */
public class Role implements Serializable {
    private static final long serialVersionUID = 4836306672907553166L;

    private Integer id;
    private String role_name;
    private String role_desc;

    private List users;

    public List getUsers() {
        return users;
    }

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

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getRole_name() {
        return role_name;
    }
    public void setRole_name(String role_name) {
        this.role_name = role_name;
    }
    public String getRole_desc() {
        return role_desc;
    }
    public void setRole_desc(String role_desc) {
        this.role_desc = role_desc;
    }

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", role_name='" + role_name + '\'' +
                ", role_desc='" + role_desc + '\'' +
                ", users=" + users +
                '}';
    }
}

     3.编写接口

package com.qcby.mapper;
import com.qcby.domain.Role;
import java.util.List;

public interface RoleMapper {
    public List findAll();
}

     4.编写配置文件




    

    
        
        
        
        
            
        
    
   

    5.编写测试方法

@Test
public void run() throws Exception {
    // 加载配置文件
    InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
    // 创建工厂对象
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    // 构建session
    SqlSession session = factory.openSession();
    RoleMapper mapper = session.getMapper(RoleMapper.class);
    List list = mapper.findAll();
    for (Role role : list) {
        System.out.println(role);
    }
    // 释放资源
    session.close();
    in.close();
}

五、MyBatis延迟加载策略

1. 延迟加载的概念

  1. 立即加载和延迟加载的区别,使用一对多的环境举例子。
  2. 立即加载:当前查询用户的时候,默认也把该用户所拥有的帐户信息查询出来了。
  3. 延迟加载:当前查询用户的时候,没有把该用户所拥有的帐户信息查询出来,而是使用帐户数据的时候,再去查询账户的数据。

2. 立即加载和延迟加载的应用场景

  1. 例如查询账户的时候,可以直接把用户查询出来,即查询多对一,这个时候可以选择立即加载。
  2. 例如查询用户的时候,可以先不查账号信息,等需要使用帐户信息的时候,再去查询,选择延迟加载。

3. 多对一的延迟加载查询演示

  1. 在AccountMapper接口中编写方法
public List findAll();

     2.编写配置文件和sql语句




    
    
    
    
    
    
    
    
    
        
        
        
        
        
            
            
            
            
            
        
    
    

     3.在UserMapper接口中编写方法

 public User findById(Integer uid); 

     4.编写配置文件UserMapper.xml


        select * from user
    
    
    
    
        
        
        
        
        
        
        
            
            
            
        
        

     4.在AccountMapper接口中编写方法

public List findByUid(Integer uid);

     5.在AccountMapper.xml配置文件中编写配置和SQL语句


    

     6.在SqlMapConfig.xml配置文件中开启延迟加载的配置


    
    
    
    

     7.编写测试方法

/**
 * 一对多缓存查询
 * @throws Exception
 */
@Test
public void run3() throws Exception {
    // 加载配置文件
    InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
    // 创建工厂对象
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    // 构建session
    SqlSession session = factory.openSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    // 调用方法
    List list = mapper.findAll();
    for (User user : list) {
        System.out.println(user.getUsername());
        System.out.println(user.getAccounts());
        System.out.println("==============");
    }
    // 释放资源
    session.close();
    in.close();
}
  

六、MyBatis框架的缓存

1. 缓存的概念

  1. 缓存的概念
    1. 在内存中临时存储数据,速度快,可以减少数据库的访问次数。
    2. 经常需要查询,不经常修改的数据,不是特别重要的数据都适合于存储到缓存中。

2. MyBatis的一级缓存

  1. MyBatis的一级缓存也是SqlSession的缓存,默认开启。
  2. SqlSession对象中维护了一个Map集合,用于存储相互的缓存数据。
  3. 查询的时候,先从SqlSession的缓存中查找,如果有,直接返回。如果没有,查询数据库。
  4. 证明一级缓存的存在,通过用户id查询2次,查看结果,sql语句只会执行一次。
 /**
 * 测试一级缓存是否存在
 * @throws Exception
 */
@Test
public void run1() throws Exception {
    // 加载配置文件
    InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
    // 创建工厂对象
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    // 构建session
    SqlSession session = factory.openSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.findById(1);
    System.out.println(user);
    User user2 = mapper.findById(1);
    System.out.println(user2);
    // 释放资源
    session.close();
    in.close();
}

   5.一级缓存的原理分析

  1. 一级缓存底层使用的是Map集合,key存储的是执行的SQL语句,value存放的是查询的对象
  2. BaseExecutor类的152行源码地方先查询缓存,再查询数据库。
  3. 一级缓存的生命周期和SqlSession的生命周期相同,SqlSession对象关闭,一级缓存也会关闭。

        session.clearCache();调用该方法可以清空缓存

        调用调用SqlSession的update、insert、delete、commit和close等方法的时候也会清空缓存。

3、MyBatis的二级缓存

SqlSessionFactory级别,通过配置开启,对一级缓存的增强,之创建一次,通过tomcat销毁,一般不使用,用redis

全局配置文件SqlMapConfig.xml中开启

  
    
    

在mapper中配置


 

测试

/**
 * 测试二级级缓存是否存在
 * @throws Exception
 */
@Test
public void run1() throws Exception {
    // 加载配置文件
    InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
    // 创建工厂对象
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    // 构建session
    SqlSession session = factory.openSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.findById(1);
    System.out.println(user);
    session.close();
    SqlSession sqlSession1=factory.openSession();
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    User user2 = mapper1.findById(1);
    System.out.println(user2);
    // 释放资源
    session.close();
    in.close();
}

你可能感兴趣的:(mybatis,数据库)