MyBatis

 

1. java项目的基本三层架构

 

 

 

2. 框架和库的区别

  • 框架: 是一整套技术解决方案

  • 库:是对原有技术的封装 让操作更加简单而已

3.Mybatis的介绍

  • MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github,通俗说法Ibatis3 = MyBatis

  • iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)

  • MyBatis是一个数据持久层(ORM)框架。把实体类和SQL语句之间建立了映射关系,是一种半自动化的ORM实现

  • MyBatis的优点:

  • 减少代码量

  • 基于SQL语法,简单易学

  • 能了解底层组装过程

  • SQL语句封装在配置文件中,便于统一管理与维护,降低了程序的耦合度

  • 程序调试方便

4. MyBatis的获取

  • 第一种方式 从官网(也是跳转到github)

  • 第二种方式 github

5. mybatis的目录介绍

 

 

 

6. Mybatis操作数据库

6.1. 环境搭建

6.1.1. 建库建表

create database mybatis;
use mybatis;
​
​
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` date DEFAULT NULL COMMENT '生日',
  `sex` char(1) DEFAULT NULL COMMENT '性别',
  `address` varchar(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;

 

6.1.2. 插入数据

insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`)
values (1,'盛世名',NULL,'2',NULL),
(10,'张甲吉','2014-07-10','1','北京市'),(16,'张吉东',NULL,'1','河南郑州'),
(22,'孙训',NULL,'1','河南郑州'),(24,'周芷若',NULL,'1','河南郑州'),
(25,'赵敏',NULL,'1','河南郑州'),(26,'小昭',NULL,NULL,NULL);

 

6.1.3. 创建java项目

6.1.4. 导入jar包(新建lib)

导入mybatis和mybatis的依赖包

导入mysql的驱动包

注意: 直接导入mybatis 和 mysql驱动包 2个包就可以了 但是建议把所有的包都导入进去

6.1.5. 创建实体类

package com.shangma.cn.entity;
​
import java.util.Date;
​
public class User {
    private int id;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址
public int getId() {
        return id;
    }
​
    public void setId(int id) {
        this.id = id;
    }
​
    public String getUsername() {
        return username;
    }
​
    public void setUsername(String username) {
        this.username = username;
    }
​
    public String getSex() {
        return sex;
    }
​
    public void setSex(String sex) {
        this.sex = sex;
    }
​
    public Date getBirthday() {
        return birthday;
    }
​
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
​
    public String getAddress() {
        return address;
    }
​
    public void setAddress(String address) {
        this.address = address;
    }
​
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                ", address='" + address + '\'' +
                '}';
    }
}

6.1.6. 编写主配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!--mybaits全局配置文件的约束-->
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
​
​
<!--配置信息 -->
<configuration>
    <environments default="development">
        <environment id="development">
            <!--jdbc的事务管理 交给mybatis管理  -->
            <transactionManager type="JDBC"/>
            <!--数据源 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="rootroot"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--<mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
    </mappers>
</configuration>

 

 

6.1.7. 编写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="">
 
</mapper>

 

6.1.8. 基本目录结构如下

 

6.2. 增删改查

6.2.1. 根据id查询

6.2.1.1. 修改映射文件
<?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的操作进行分类化管理  有点类似包管理
-->
<mapper namespace="huige"><!--是根据id查询用户
    select标签表示查询
          id属性: 要求在一个namespace当中唯一 id随意写 但是要唯一
          parameterType表示参数的java类型
          resultType  表示查询后的返回值java类型
          #{id}  表示占位符  传过来是什么  id就等于什么  如果是基本数据类型 这个id可以随意写
    -->
    <select id="findUserById" parameterType="int" resultType="com.shangma.cn.entity.User">
        select * from user where id=#{id}
    </select>
</mapper>

 

6.2.1.2. 修改配置文件

在 mappers标签中添加如下内容

    <mappers>
        <!--mapper映射文件 resource 表示从src 下(也就是类路径下 开始找mapper.xml文件夹的路径,使用的是文件夹路径的形式“/”而不是“.”)-->
         <mapper resource="com/sm/qy30/mapper/userMapper.xml"/>
    </mappers>

 

6.2.1.3. 编写代码
public class TestDemo {
​
    /**
     * 通过id 查询
     */
    @Test
    public void  selectById() throws IOException {
        //配置文件路径
        String resource = "mybatis.xml";
        //通过路径加载配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //获得sqlsessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
         //获得sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 操作数据库
        User o = sqlSession.selectOne("huige.findUserById", 10);
        //打印结果
        System.out.println(o);
         //关闭资源
        sqlSession.close();
​
    }
​
}

 

6.2.1.4. 结果打印
6.2.1.5. 警告说明

 

 

 

6.2.2. 添加用户

6.2.2.1. 修改映射文件

 
  <!--
      添加用户
      参数类型 是引用类型
      则 #{}中的值 不能胡写  要对应成 实体类中的属性名 
     --><insert id="addUser" parameterType="com.shangma.cn.entity.User">
        insert into user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>

 

 

6.2.2.2. 加载映射文件

已经加载过

6.2.2.3. 编写代码
 @Test
    public  void  addUser() throws IOException {
        String path  = "mybatis.xml";
        InputStream in  = Resources.getResourceAsStream(path);
        SqlSessionFactory factory  = new SqlSessionFactoryBuilder().build(in);
        SqlSession sqlSession = factory.openSession();
        User user = new User();
        user.setUsername("辉哥");
        user.setAddress("863中部软件园");
        user.setBirthday(new Date());
        user.setSex("男");
        sqlSession.insert("huige.addUser",user);
        //要提交事务  不提交事务 则可能没有真正的持久化硬盘中
        sqlSession.commit();
        sqlSession.close();
​
    }
​

6.2.3. 修改用户

6.2.3.1. 修改映射文件
 <!-- 修改用户 --><update id="updateUser" parameterType="com.shangma.cn.entity.User">
​
        update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
​
    </update>
6.2.3.2. 加载映射文件

已经加载过

6.2.3.3. 编写代码
 @Test
    public  void  updateUser() throws IOException {
        String path  = "mybatis.xml";
        InputStream resourceAsStream = Resources.getResourceAsStream(path);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
​
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //先查出来 再修改
        User o = sqlSession.selectOne("huige.findUserById", 10);
        o.setAddress("尚马教育");
        o.setSex("女");
        sqlSession.update("huige.updateUser",o);
        sqlSession.commit();
        sqlSession.close();
​
    }

6.2.4. 删除用户

6.2.4.1. 修改映射文件
<!--删除用户--><delete id="deleteUser" parameterType="int">
​
        delete from  user where id = #{id}
​
    </delete>

 

6.2.4.2. 加载映射文件

加载过了

6.2.4.3. 编写代码
 @Test
    public  void  deleteUser() throws IOException {
        String path  = "mybatis.xml";
        InputStream resourceAsStream = Resources.getResourceAsStream(path);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.delete("huige.deleteUser",10);
        //只要不是查询  赠删改 都需要提交事务
        sqlSession.commit();
        sqlSession.close();
    }

6.2.5. 模糊查询

6.2.5.1. 修改映射文件
 <!--模糊查询 ${}
   如果参数为简单类型时,${}里面的参数名称必须为value
    -->
    <select id="searchUser" parameterType="string" resultType="com.shangma.cn.entity.User">
        select * from  user  where username like '%${value}%'
    </select>
6.2.5.2. 加载映射文件

加载过了

6.2.5.3. 编写代码
@Test
    public  void  searchUser() throws IOException {
        String path  = "mybatis.xml";
        InputStream resourceAsStream = Resources.getResourceAsStream(path);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<User> users = sqlSession.selectList("huige.searchUser", "小");
         users.forEach(user -> System.out.println(user));
        sqlSession.close();
​
    } 

6.2.6. 添加用户返回主键

6.2.6.1. 修改映射文件
<!--
      添加用户
      参数类型 是引用类型
      则 #{}中的值 不能胡写  要对应成 实体类中的属性名
     --><insert id="addUser" parameterType="com.shangma.cn.entity.User">
     
            <!--
           keyProperty 表示主键的属性
           order  表示返回的主键是在添加前 还是添加后   取值after   before  
          resultType 表示返回值类型 
          last_insert_id()  是mysql中的函数  返回的是最后一次添加的id
     -->
  <selectKey keyProperty="id" order="AFTER" resultType="int">
    select last_insert_id()
​
  </selectKey>
        insert into user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>

7. 面临的问题

  • 问题一:代码中 存在大量的重复代码

  • 问题二:我们mybatis是持久层框架属于dao的部分

生命周期介绍

 

 

 

8. 简单的封装

/**
 * 对重复的代码进行简单封装
 */
public class TestDemo2 {
    SqlSessionFactory sqlSessionFactory;
    SqlSession sqlSession;
    InputStream inputStream;
​
    @Before
    public void init() throws IOException {
        System.out.println("init方法打印了");
        //配置文件路径
        String resource = "mybatis.xml";
        //通过路径加载配置文件
        inputStream = Resources.getResourceAsStream(resource);
        //获得sqlsessionFactory
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        sqlSession = sqlSessionFactory.openSession();
    }
​
    /**
     * 通过id 查询
     */
    @Test
    public void  selectById() throws IOException {
         //获得sqlSession
// 操作数据库
        User o = sqlSession.selectOne("huige.findUserById", 30);
        //打印结果
        System.out.println(o);
​
​
    }
​
    @Test
    public  void  addUser() throws IOException {
        User user = new User();
        user.setUsername("辉哥");
        user.setAddress("863中部软件园");
        user.setBirthday(new Date());
        user.setSex("男");
        sqlSession.insert("huige.addUser",user);
​
​
​
    }
​
    @Test
    public  void  updateUser() throws IOException {
        //先查出来 再修改
        User o = sqlSession.selectOne("huige.findUserById", 30);
        o.setAddress("尚马教育");
        o.setSex("女");
        sqlSession.update("huige.updateUser",o);
    }
​
    @Test
    public  void  deleteUser() throws IOException {
        sqlSession.delete("huige.deleteUser",10);
    }
​
​
​
    @Test
    public  void  searchUser() throws IOException {
        List<User> users = sqlSession.selectList("huige.searchUser", "小");
        users.forEach(user -> System.out.println(user));
    }
​
​
    @After
    public void release(){
        System.out.println("release方法打印了");
        sqlSession.commit();
        sqlSession.close();
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

其实每次操作数据库时 先执行init 操作完数据库之后 又执行release 也就是说 每次都会初始化一个sqlSession 如果全局就一个sqlsession时 这种方式 就不可以了

9. myBatis的dao开发

9.1. 常见dao接口

package com.shangma.cn.dao;
​
import com.shangma.cn.entity.User;
​
public interface UserDao {
​
    //根据id查询
    User findById(Integer id);
    //添加用户
    void  addUser(User user);
}

9.2. 创建实现类

package com.shangma.cn.dao.impl;
​
import com.shangma.cn.dao.UserDao;
import com.shangma.cn.entity.User;
import org.apache.ibatis.session.SqlSession;
​
public class UserDaoImpl implements UserDao {
    private SqlSession sqlSession;
​
​
    public UserDaoImpl(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }
​
    @Override
    public User findById(Integer id) {
        // 操作数据库
        User o = sqlSession.selectOne("huige.findUserById", 30);
        return o;
    }
​
    @Override
    public void addUser(User user) {
        sqlSession.insert("huige.addUser",user);
    }
}

 

9.3. 编写测试类

public class TestDemo3 {
    SqlSessionFactory sqlSessionFactory;
    SqlSession sqlSession;
    UserDao userDao;
    InputStream inputStream;
​
​
    @Before
    public void init() throws IOException {
        System.out.println("init方法打印了");
        //配置文件路径
        String resource = "mybatis.xml";
        //通过路径加载配置文件
         inputStream = Resources.getResourceAsStream(resource);
        //获得sqlsessionFactory
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        sqlSession = sqlSessionFactory.openSession();
        userDao = new UserDaoImpl(sqlSession);
    }
​
    /**
     * 通过id 查询
     */
    @Test
    public void  selectById() throws IOException {
        System.out.println(userDao.findById(30));
    }
​
    @Test
    public  void  addUser() throws IOException {
        User user = new User();
        user.setUsername("凤姐");
        user.setAddress("天堂地狱");
        user.setBirthday(new Date());
        user.setSex("女");
        userDao.addUser(user);
        System.out.println(user.getId());
    }
​
​
    @After
    public void release(){
        System.out.println("release方法打印了");
        sqlSession.commit();
        sqlSession.close();
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 


10.myBatis的Mapper开发(重点)

Mapper代理的方式 主要是解决 不写dao实现类 程序员只需要编写Mapper接口就可以了 但满足规范时 MyBatis 会自动生成Mapper的代理实现类 操作

10.1. Mapper代理书写规范

  • Mapper接口的完整路径 要和Mapper中的namespace一致

  • Mapper接口中的方法 要和映射文件中select update insert delete标签中的 id值一致

  • Mapper接口中方法的参数只能有一个 并且类型要和映射文件中select update insert delete标签中的parameterType的类型一致 、

  • Mapper接口中返回值要和要和映射文件中select update insert delete标签中的resultType或者resultMap的类型一致

10.2. 编写Mapper接口

public interface UserMapper {
​
    //查询所有
    List<User> selectAll();
​
    //根据id查询
    User selectById(Integer id);
​
​
    //添加用户
    void saveUser(User user);
}
​

 

 

10.3. 编写配置文件

<?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.shangma.cn.mapper.UserMapper">
<!--查询所有 -->
    <select id="selectAll" resultType="com.shangma.cn.entity.User">
     select * from user;
    </select><!--根据id查询-->
    <select id="selectById" parameterType="java.lang.Integer" resultType="com.shangma.cn.entity.User">
        select * from  user  where id = #{id}
    </select>
    <!--添加用户-->
    <insert id="saveUser" parameterType="com.shangma.cn.entity.User">
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            select last_insert_id();
        </selectKey>
        insert into user(username,sex,birthday,address) values(#{username},#{sex},#{birthday},#{address})
​
    </insert>
</mapper>
 

 

10.4. 编写测试类

public class MapperTest {
    
    @Test
    public  void  testMapper() throws IOException {
        String path = "mybatis.xml";
        InputStream in = Resources.getResourceAsStream(path);
​
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
​
        SqlSession sqlSession = sqlSessionFactory.openSession(true);//设置为true时 自动提交事务
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //查询所有
//        List<User> users = mapper.selectAll();
//        users.forEach(user -> System.out.println(user));
//根据id查询
//System.out.println(mapper.selectById(30));
// 添加
        User user  = new User();
        user.setUsername("腾格尔");
        user.setBirthday(new Date());
        user.setSex("男");
        mapper.saveUser(user);
        sqlSession.close();
        in.close();
    }
}

 

11.全局配置文件讲解

11.1. 名字的规范

全局配置文件的名字 可以随意写 想怎么写 怎么写 但是一般的情况下 会叫如下几个名字

  • sqlMapperConfig.xml

  • mybatis-config.xml

11.2. 数据源问题

 

 

 

11.3. 连接信息抽出

11.3.1. 编写properties文件

 

 

 

11.3.2. 加载配置文件

 

 

 

11.3.3. 读取配置文件

 

 

 

11.4. 别名问题(了解)

我们的 参数类型 和 返回值类型 如果是引用类型 可以起别名

11.4.1. 起别名

  • 方式一:

    <typeAliases>
        <typeAlias type="com.shangma.cn.entity.User" alias="user"/>
    </typeAliases>

  • 方式二:

     <typeAliases>
         <!--指定包名时 别名就是包中对应类的类名 不区分大小写-->
       <package name="com.shangma.cn.entity"/>
    </typeAliases>

11.4.2. 使用别名

 

 

 

11.5. mapper的属性值

 

 

 

11.5.1. resource属性

这个属性的值 是类路径下的资源

   <mappers>
       <!--mapper的写法 resource 表示从src下(类路径下 开始找 使用文件夹的形式 而不是.)-->
       <mapper resource="com/shangma/cn/mapper/user.xml"/>  
   </mappers>

11.5.2. url属性

这个属性的值 表示完整路径 带盘符 带协议

    <mappers>
<mapper url="file:///F:\mybaits01\src\com\shangma\cn\mapper\user.xml"/>
   </mappers>

 

11.5.3. class属性

这个属性值 表示 mapper接口的完整路径

要求:接口名称要和mapper文件名一致 并且在同一个目录下

11.5.4. 开发中的写法

直接写包名 这样的话 如果mapper映射文件比较多的时候 可以统一设置

不需要再一个一个单独设置了

要求:接口名称要和mapper文件名一致 并且在同一个目录下

    <mappers>
 
       <package name="com.shangma.cn.mapper"/>
   </mappers>

12. 映射文件

  • 12.1. parameterType

    • 表示参数类型

    • 取值

      • 基本数据类型

      • map

      • 自定义类型

      • 包装类

    12.1.1. 基本数据类型

        
    <!--
         基本数据类型 
          parameterType="java.lang.Integer" 可以写包装类 类的完整路径
          parameterType="int"   可以写别名   
        -->
        <select id="selectById" parameterType="int" resultType="user">
            select * from  user  where id = #{id}
        </select>

     

    12.1.2. 实体类

     <!--
        如果是实体类 
        parameterType="com.shangma.cn.entity.User" 写类的完整路径 
        如果起了别名
          例如 
             <typeAliases>
                <package name="com.shangma.cn.entity"/>
              </typeAliases> 
              
          则可以写别名  
         parameterType="user"
         
         则使用时 #{}是这个实体类中的属性名 
        
        -->
        <insert id="saveUser" parameterType="user">
            <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
                select last_insert_id();
            </selectKey>
            insert into user(username,sex,birthday,address) values(#{username},#{sex},#{birthday},#{address})
        </insert>

     

    12.1.3. 包装类

    • 创建包装类

      public class UserExt {
      ​
          private User user;
      ​
          private String like="辉";
      ​
          public User getUser() {
              return user;
          }
      ​
          public void setUser(User user) {
              this.user = user;
          }
      ​
          public String getLike() {
              return like;
          }
      ​
          public void setLike(String like) {
              this.like = like;
          }
      ​
          @Override
          public String toString() {
              return "UserExt{" +
                      "user=" + user +
                      ", like='" + like + '\'' +
                      '}';
          }
      }

       

    • 在userMapper中添加内容

        
        // 模糊查询
          List<User> searchUser(UserExt userExt);

       

    • 编写映射文件

        <select id="searchUser" parameterType="com.shangma.cn.entity.UserExt" resultType="com.shangma.cn.entity.User">
              select * from user where sex=#{user.sex} and username like '%${like}%'
          </select>

       

    • 测试

       @Test
          public  void  testMapper() throws IOException {
              String path = "mybaits-config.xml";
              InputStream in = Resources.getResourceAsStream(path);
      ​
              SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
      ​
              SqlSession sqlSession = sqlSessionFactory.openSession(true);//设置为true时 自动提交事务
      //getMapper表示底层使用了动态代理
              UserMapper mapper = sqlSession.getMapper(UserMapper.class);
              UserExt userExt  = new UserExt();
              User user = new User();
              user.setSex("男");
              userExt.setUser(user);
              List<User> users = mapper.searchUser(userExt);
              users.forEach(user1 -> System.out.println(user1));
      ​
              sqlSession.close();
              in.close();
          }

       

    12.2. resultType

    返回值类型

    注意点:返回单个实体类和实体类集合时 mapper映射文件中的resultType的类型是一样的

    问题:这种单独resultType的方式 依赖于 属性名和表中的列名一致 所以数据会封装到实体类中

    12.3. resultMap

    如果表中的字段和实体类中的属性名不一致时 我们需要使用 resultMap

    • 第一步 修改表中字段

       

       

    • 第二步:使用resultmap 设置属性和别名的对应关系

        
       <!--
          id属性  表示resultmap的唯一标识  不能重复  唯一的  随意写
          type  表示返回值的类型  或者可以理解为 指定的是哪个类
      ​
          result 表示  实体类属性和表的别名的对应关系
          column:表示表的列明
          property: 表示实体类属性名
      ​
          如果列是id的话 可以使用id标签
          --><resultMap id="huige" type="user">
              <id column="id_" property="id"/>
              <!--<result column="id_" property="id"/>-->
              <result column="address_" property="address"/>
          </resultMap>

       

    • 第三步 : 使用resultMap

       

       

       

13. sql 代码片

13.1. 问题演示

 

 

 

13.2. if的写法


    <select id="searchUser" parameterType="com.shangma.cn.entity.UserExt" resultMap="huige">
        select *  from user where 1=1
        <if test="user!=null">
           and sex=#{user.sex}
        </if>
        <if test="like!=null and like != '' ">
          and  username like '%${lile}%'
        </if>
    </select>

 

 

13.3. where的写法

 <select id="searchUser" parameterType="com.shangma.cn.entity.UserExt" resultMap="huige">
         select *  from user
        <where>
          <if test="user!=null">
             and sex=#{user.sex}
          </if>
          <if test="like!=null and like != '' ">
             and  username like '%${lile}%'
          </if>
        </where>
    </select>

 

13.4. 其他写法

 

 

 

13.5. foreach 标签使用

  • 使用场景 : 查询多个id的用户信息

    select * from user where id in(x,x,x)

  • mapper接口中添加方法

     List<User> selectByIds(UserExt userExt);
  • 编写mapper文件

    <!--
      collection 要遍历的集合
      open 表示开始的内容
      close 表示结束的内容
      separator 表示分割的内容
      item  表示每次遍历出来的内容
      --><select id="selectByIds" parameterType="com.shangma.cn.entity.UserExt" resultMap="huige">
          select * from user where id_ in
​
          <foreach collection="ids" open="(" close=")" separator="," item="id">
              #{id}
          </foreach>
      </select>

 

  • 测试

      @Test
        public  void  testMapper() throws IOException {
            String path = "mybaits-config.xml";
            InputStream in = Resources.getResourceAsStream(path);
    ​
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    ​
            SqlSession sqlSession = sqlSessionFactory.openSession(true);//设置为true时 自动提交事务
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<Integer> list  = new ArrayList<>();
            list.add(28);
            list.add(29);
            list.add(30);
            list.add(31);
            UserExt userExt  = new UserExt();
            userExt.setIds(list);
            List<User> users = mapper.selectByIds(userExt);
            users.forEach(user1 -> System.out.println(user1));
    ​
            sqlSession.close();
            in.close();
        }

     

14. mybatis中的多表关系

实际开发中 表和表之间存在一定的关系 这种关系不一定需要数据库中的约束 例如外键 表的关系 有如下几种

  • 一对一

  • 一对多

  • 多对多

14.1. 环境搭建

14.1.1. 建表建库

CREATE TABLE `cart`  (
  `cartId` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `userId` int(11) NULL DEFAULT NULL,
  `totalnum` int(11) NULL DEFAULT NULL,
  `totalmoney` double(255, 0) NULL DEFAULT NULL,
  PRIMARY KEY (`cartId`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
​
INSERT INTO `cart` VALUES ('4275a23c41694cc0ae54f2a8d97b024b', 5, 1, 129);
INSERT INTO `cart` VALUES ('9e96b8b3a71c4700bfc0a4b164a3887e', 4, 3, 9664);
​
​
CREATE TABLE `cartitem`  (
  `cartItemId` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `cartId` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `pid` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `pnum` int(11) NULL DEFAULT NULL,
  `pmoney` double(255, 0) NULL DEFAULT NULL,
  PRIMARY KEY (`cartItemId`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
​
​
INSERT INTO `cartitem` VALUES ('5c8334e949eb471ca32d8d2c2d5db9ed', '9e96b8b3a71c4700bfc0a4b164a3887e', '1d062d5fcaf147468aa325af6715df18', 1, 5666);
INSERT INTO `cartitem` VALUES ('b6a42a8be7584c269399d67b3d2b7452', '9e96b8b3a71c4700bfc0a4b164a3887e', '082d077043e74f08b5c8e82c33990d05', 2, 3998);
INSERT INTO `cartitem` VALUES ('ff81edf0755d4f5e8e5475745185de2a', '4275a23c41694cc0ae54f2a8d97b024b', '4e6bc5c119a848da8678e6a4d3ec1057', 1, 129);
​
​
​
CREATE TABLE `good`  (
  `pid` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `pname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `price` double(10, 2) NULL DEFAULT NULL,
  `pimg` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `pdesc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
​
​
INSERT INTO `good` VALUES ('082d077043e74f08b5c8e82c33990d05', '手机', 1999.00, '/images/shouji.jpg', '手机很快');
INSERT INTO `good` VALUES ('1b64510f93104a7886c334351e465b80', '保暖内衣', 56.00, '/images/baonuan.jpg', '很暖和');
INSERT INTO `good` VALUES ('1d062d5fcaf147468aa325af6715df18', '电视', 5666.00, '/images/dianshi.jpg', '1080P');
INSERT INTO `good` VALUES ('224c796995744ae5a22f6d8d72eefc45', '电脑', 8888.00, '/images/diannao.jpg', '128G内存');
INSERT INTO `good` VALUES ('46ea4fabaf8741a3a455e30c83607086', '卫衣', 99.00, '/images/weiyi.jpg', '裹得很严实');
INSERT INTO `good` VALUES ('4e6bc5c119a848da8678e6a4d3ec1057', '吹风机', 129.00, '/images/chuifengji.jpg', '风很大');
​
​

 

 

14.1.2. 表关系介绍

  • 一个用户只有一个购物车 -> 一对一

  • 一个购物车有多个购物车项 -> 一对多

  • 一个购物车项 对应一个商品 -> 一对一

  • 一个用户可以勾选多个商品 一个商品也可以被多个用户勾选 -> 多对多

14.1.3. 编写实体类

  使用lombok ,以注解的方式自动生成get、set方法和重写 tostring方法,还有有参和无参的构造器

  首先,在idea下载lombok插件,并设置

 

 

 在mavenrepository  (https://mvnrepository.com/)下载jar包

@Data  生成get、set方法和重写tostring方法
@NoArgsConstructor 生成无参构造 @AllArgsConstructor 生成有参构造
  • 购物车

    package com.sm.qy30.entity;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /**
     * @author JAVASM
     */
    @Data
    @NoArgsConstructor @AllArgsConstructor
    public class Cart { ​ private String cartId;//购物车id private Integer userId; //用户id private int totalnum; //总数量 private double totalmoney;//总钱数 ​ }

     ​

  • 购物车中的每一条

    package com.sm.qy30.entity;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /**
     * @author JAVASM
     */
    @Data
    @NoArgsConstructor @AllArgsConstructor
    public class CartItem { ​ private String cartItemId; //购物车项id private String cartId;//购物车id private String pid; //商品id private int pnum; //商品数量 private double pmoney; // 小计 ​ }
  • 商品

    package com.sm.qy30.entity;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /**
     * @author JAVASM
     */
    @Data
    @NoArgsConstructor @AllArgsConstructor
    public class Good { ​ private String pid; //商品id private String pname; //商品名称 private double price; //商品价格 private String pimg; //商品图 private String pdesc;//商品描述 ​ }

     


14.2. 一对一的写法

  • 需求 查询用户以及他的购物车信息

14.2.1. 修改User实体类

 

 

 

14.2.2. 编写Mapper

 

 

 

14.2.3. 编写映射文件

 

 

 

14.2.4. 测试以及结果

 

 

 

14.3. 一对多的写法

  • 需求: 查询购物车以及购物车中的项

14.3.1. 修改购物车实体类

 

 

 

14.3.2. 编写Mapper

 

 

  

14.3.3. 编写映射文件

 

 

 

14.3.4. 测试结果

 

 

 

14.4. 多对多的写法

14.4.1. 修改cartItem实体类

 

 

 

14.4.2. 编写Mapper

 

 

 

14.4.3. 编写映射文件

 

 

 

15. mybatis的延迟加载

延迟加载 又叫懒加载 表示在关联查询中 当使用关联的数据时 再去加载 没有使用 则先不加载 提交数据库的性能 和效率

resultMap中的association和collection标签具有延迟加载的功能。

拿购物车和购物车详情为例,但是注意 之前的写法是内连接的方式 一下子全部查出来了 我们还要改造我们的映射文件

15.1. setting配置

 

 

 

15.2. 编写cartItemMapper

 

 

 

15.3. 编写映射文件

 

 

 

 

15.4. 修改cart的映射文件

 

 

 

15.5. 测试延迟加载

16. mybatis的缓存

16.1. 缓存的了解

所有的缓存 都是一个思想 数据存在一个地方 (例如redis中)

  • 查询数据 获得结果 此时存缓存的地方还没有数据

  • 把查询出的结果 放到存缓存的地方 让有数据

  • 再查询时 先判断 这个地方有没有数据 如果有 直接拿出来 则不会再查数据库

  • 当修改时 清空缓存

 

16.2. Mybatis的一级缓存

mybatis的一级缓存 存在内存中 是sqlSession级别的缓存 缓存的结构是个Map集合

map的key 是sql语句 条件 等等信息组成的唯一值

map的value 就是查询出来的结果

mybatis的一级缓存 默认是开启的

 

 

 

16.2.1. 一级缓存的演示

编写一个查询所有的mapper和映射文件

 

 

 

16.3. myBatis的二级缓存

mybatis的二级缓存 是 sqlsessionFactory级别的缓存 或者说是Mapper或者说 namespace级别的缓存

当使用查询时,查询结果会存入对应的namespace中.

当所属namespace使用增删改时,会清空该namespace中的缓存

二级缓存可能会存入内存,也可能会存入硬盘

由于二级缓存可能会存入硬盘,所以需要将对应需要缓存的实体类进行序列化(implements Serializable)。

myBatis的二级缓存 高版本默认是开启的

16.3.1. 演示二级缓存

  • 修改实体类

 

 

 

  • 修改映射文件

     

     

  • 测试

     

     

     

16.3.2. 局部不使用缓存

 

 

 

16.3.3. 刷新二级缓存

默认情况下

如果是select语句,那么flushCache是false。

如果是insert、update、delete语句,那么flushCache是true。

如果查询语句设置成true,那么每次查询都是去数据库查询,即意味着该查询的二级缓存失效。

如果查询语句设置成false,即使用二级缓存,那么如果在数据库中修改了数据,

而缓存数据还是原来的,这个时候就会出现脏读。

 

 

 

17. Mybatis的注解开发 (了解 )

使用注解开发 可以不用写 映射文件

17.1. 普通的crud

public interface UserMapper {
​
    /**
     * 查询所有
     *
     * @Select 注解 表示查询 代替映射文件中的select标签
      */
​
    @Select("select * from user")
    public List<User> selectAll();
​
​
    /**
     * 根据id查询
     * @return
     */
    @Select("select * from user where id = #{userId}")
    public  User selectUserById(Integer userId);
​
​
    /**
     * 添加用户
     * @return
     */
    @Insert("insert into user(username,sex,birthday,address) values(#{username},#{sex},#{birthday},#{address})")
    public  void  addUser(User user);
​
​
    /**
     * 修改用户
     * @return
     */
​
    @Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}")
    public  void  updateUser(User user);
​
    /**
     * 删除用户
     * @param userId
     */
    @Delete("delete from user where id = #{userId}")
    public  void deleteUserById(Integer userId);
​
}

 

17.2. 属性名和列名不一致的情况

 

 

 

 

18. 探索驼峰命名的规则

18.1. 面临的问题

 

 

18.2. 开启驼峰命名

虽然我们可以使用resultMap或者result注解进行匹配 但是这样做复杂 实际开发中 遵循驼峰命名非常必要

 

 

 

  @Test
   public  void  testMapper() throws IOException {
       String path = "mybaits-config.xml";
       InputStream in = Resources.getResourceAsStream(path);

       SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);

       SqlSession sqlSession = sqlSessionFactory.openSession(true);//设置为true时 自动提交事务
       UserMapper mapper = sqlSession.getMapper(UserMapper.class);
       List<Integer> list  = new ArrayList<>();
       list.add(28);
       list.add(29);
       list.add(30);
       list.add(31);
       UserExt userExt  = new UserExt();
       userExt.setIds(list);
       List<User> users = mapper.selectByIds(userExt);
       users.forEach(user1 -> System.out.println(user1));

       sqlSession.close();
       in.close();
  }
posted @ 2021-05-27 14:27  mini9264  阅读(11)  评论(0编辑  收藏  举报