初学MyBatis

MyBatis简介

引言

  1. Mybatis:是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到goodle code,并且改名为Mybatis,2013年11月迁移到Github
  2. Mybatis:是一个实现了数据持久化的开源框架,底层是对JDBC进行封装
  3. Mybatis:是半自动映射框架
  4. Mybatis是ORM框架(object、relational、mapping)对象关系模型,使用Mybatis,可以面向对象来操作持久化对象,ORM框架会通过映射关系将这些面向对象的操作转换成底层的SQL操作

持久化

  1. 数据持久是将内存中的数据保存到磁盘上加以固
    化的过程
  2. 持久化的实现过程大多通过各种关系型数据库来完成。

持久层

  1. 在Java开发中对数据进行持久化的集合

第一个MyBatis

环境搭建

  • 新建Maven工程,pom.xml

           <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.22</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.6</version>
            </dependency>
    		<!-- 解决找不到资源文件配置 -->
    		<build>
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.XML</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>true</filtering>
                </resource>
            </resources>
        </build>
    
  • 新建数据表

     create table user3(
     id int primary key auto_increment,
     username varchar(11),
     password varchar(11),
     age int
     );
    
  • 新建数据表对应的实体类User(与数据表中的字段对应)

    package com.sheep.pojo;
    
    public class User {
        private long id;
        private String username;
        private String password;
        private int age;
    	...请补充get、set
    }
    
  • 创建MyBatis的核心配置文件 config.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>
        <!--  配置Mybatis运行环境  -->
        <environments default="mysql">
            <environment id="mysql">
                <!-- 配置JDBC事物管理 -->
                <transactionManager type="JDBC"/>
                <!-- POOLED配置JDBC数据源连接池 -->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url"
                              value="jdbc:mysql://localhost:3306/user01?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123321"/>
                </dataSource>
            </environment>
        </environments>
        <!-- 注册UserMapper.XML -->
        <mappers>
            <mapper resource="com/sheep/mapper/UserMapper.XML"/>
        </mappers>
    </configuration>
    

原生接口开发

  1. 创建MyBatis的配置文件 Mapper.xml。MyBatis框架需要开发者自定义SQL语句,写在Mapper.xml文件中,实际开发 中,会为每个实体类创建对应的Mapper.xml,定义管理该对象数据的SQL。

  2. 调用MyBatis的原生接口执行添加操作

    <?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.sheep.mapper.UserMapper">
        <insert id="insert" parameterType="com.sheep.pojo.User">
            insert into user3(username,password,age) values(#{username},#{password},#{age})
        </insert>
    </mapper>
    
    • namespace属性通常设置为文件所在包+文件名的形式
    • id是实际调用MyBatis方法时需要用到的参数
  • parameterType是调用对应方法时参数的数据类型
    • resultType是查询结果的返回类型
    • insert标签表示执行添加操作
    • update标签表示执行更新操作
    • select标签表示执行查询操作
    • delete标签表示执行删除操作
  1. 在全局配置文件config.xml中注册user3Mapper.xml

    <!-- 注册User3Mapper。xml -->
    <mappers>
        <mapper resource="com/sheep/mapper/UserMapper.XML"/>
    </mappers>
    
  2. 调用MyBatis原生接口进行操作

    import com.sheep.pojo.User;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.InputStream;
    
    public class Test {
        public static void main(String[] args) {
            //加载配置文件
            InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("config.XML");
            //创建SessionFactoryBuilder对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            //SqlSessionFactoryBuilder对象的Build方法通过配置文件创建会话工厂SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
            //通过工厂获取SqlSession
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //通过SqlSession调用User3Mapper中的SQL语句
            //com.sheep.mapper.UserMapper.insert=com.sheep.mapper.UserMapper+.insert(statement标签的id属性)
            String statement = "com.sheep.mapper.UserMapper.insert";
            User user = new User(1L,"梅西","123123",22);
            sqlSession.insert(statement,user);
            //提交事务
            sqlSession.commit();
            //释放资源
            sqlSession.close();
        }
    }
    
  • 加载MyBatis配置文件
    • 创建SqlSessionFactoryBuilder对象
    • 通过SqlSessionFactoryBuilder对象创建会话工厂SqlSessionFactory
    • 通过工厂获取SqlSession
    • 通过SqlSession获取Mapper映射文件全类名+statement标签中的id属性调用SQL
    • 提交事务、释放资源

代理接口开发

通过MyBatis代理实现自定义接口

  1. 自定义接口

    package com.sheep.repository;
    import com.sheep.pojo.User;
    import java.util.List;
    
    public interface UserIntf {
        public int insert(User user);
        public int update(User user);
        public int deleteById(long id);
        public List<User> findAll();
        public User findById(long id);
    }
    
  2. 创建接口对应的UserIntf.XML,定义接口方法对应的SQL语句。

    statement标签(即<mapper>中的标签)可根据SQL执行的业务选择insert、delete、update、select。

    MyBatis框架会根据规则自动创建接口实现类的代理对象。

    规则:

    • Mapper.xml中namespace为接口的全类名。
    • Mapper.xml中statement的id为接口中对应的方法名。
    • Mapper.xml中statement的parameterType和接口中对应方法的参数类型一致。
    • Mapper.xml中statement的resultType和接口中对应的返回值类型一致。
    <?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.sheep.repository.UserIntf">
        <insert id="insert" parameterType="com.sheep.pojo.User">
            insert into user3(username,password,age) values(#{username},#{password},#{age})
        </insert>
        <update id="update" parameterType="com.sheep.pojo.User">
            update user3 set username=#{username},password=#{password},age=#{age} where id=#{id}
        </update>
        <delete id="deleteById" parameterType="java.lang.Long">
            delete from user3 where id=#{id}
        </delete>
        <select id="findAll" resultType="com.sheep.pojo.User">
            select * from user3
        </select>
        <select id="findById" parameterType="java.lang.Long" resultType="com.sheep.pojo.User">
            select * from user3 where id=#{id}
        </select>
    </mapper>
    
  3. 在全局配置文件config.xml中注册UserIntf.XML

    <!-- 注册User3Mapper.xml -->
        <mappers>
            <mapper resource="com/sheep/repository/UserIntf.XML"/>
        </mappers>
    
  4. 调用接口的代理对象完成相关的业务操作

    import com.sheep.pojo.User;
    import com.sheep.repository.UserIntf;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.InputStream;
    import java.util.List;
    
    public class Test2 {
        public static void main(String[] args) {
            InputStream resource = Test2.class.getClassLoader().getResourceAsStream("config.XML");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = sqlSessionFactoryBuilder.build(resource);
            SqlSession sqlSession = build.openSession();
            /**
             * 获取实现接口的代理对象
             * */
            UserIntf mapper = sqlSession.getMapper(UserIntf.class);
            /**
             * 查询
             * */
            List<User> all = mapper.findAll();
            for (User user:all){
                System.out.println(user);
            }
            sqlSession.close();
            /**
             * 添加
             * */
            User user = new User(1L, "C罗", "1235679", 34);
            mapper.insert(user);
            sqlSession.commit();
            sqlSession.close();
            /**
             * 更改
             * */
            User user2 = mapper.findById(3L);
            user.setUsername("石磊");
            user.setPassword("666666");
            user.setAge(23);
            int update = mapper.update(user2);
            sqlSession.commit();
            sqlSession.close();
            /**
             * 根据id查询对象
             * */
            User byId = mapper.findById(2L);
            System.out.println(byId);
            sqlSession.close();
            /**
             * 删除
             * */
            int i = mapper.deleteById(3L);
            System.out.println(i);
            sqlSession.commit();
            sqlSession.close();
        }
    }
    
  5. 增删改必须提交事务


MyBatis的四大神兽

SqlSessionFactory

SqlSessionFactory是MyBatis框架中十分重要的对象,它是单个数据库映射关系经过编译后的内存镜像,其主作用是创建SqlSession。SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象来创建,而SqlSessionFactoryBuilder则可以通过XML配置文件构建出SqlSessionFactory的实例

//创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象的Build方法通过配置文件创建会话工厂SqlSessionFactory
        SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(inputStream);

SqlSessionFactory对象是线程安全的,一旦被创建,在整个应用执行期间都会存在。

如果多次创建同一个数据库的SqlSessionFactory,那么数据库资源将很容易被耗尽,所有通常每一个数据库都会对应一个SqlSessionFactory,所有在构建SqlSessionFactory实例时,建议使用单例模式。

SqlSession

SqlSession是应用程序与持久层之间执行交互操作的一个单线程对象,主要用作持久化操作。

SqlSession对象包含了数据库中所有执行SQL操作的所有方法,底层封装了JDBC连接。

每一个线程都应该有一个自己的SqlSession实例,并且该实例是不能被共享的。SqlSession实例是线程不安全的,因此其使用范围最好在一次请求或一个方法中,绝不能将其放在一个类的静态字段、实例字段或任何管理范围(Servlt的HttpSession)中使用。使用完SqlSession记得关闭。

Config.xml

Config.xml配置文件中<configuration>元素是根元素,<configuration>的子元素必须按照以下顺序配置,否则会报错。

<configuration>
    <properties><!-- 数据库的连接属性 -->
    <settings><!-- 开启二级缓存、开启延时加载 -->
    <typeAliases><!-- 设置别名 -->
    <typeHandlers><!-- 设置一个参数、或者从结果集中取出一个值 -->
    <objectFactory><!-- 对象工厂 -->
    <plugins><!-- 插件 -->
    <environments><!-- 数据环境配置 -->
    <databaseIdProvider>
    <mappers><!-- 指定Mapper文件的映射位置 -->
</configuration>

Mapper.xml

  • statement标签:<select>、<insert>、<update>、<delete>、<sql>、<cache>、<cache-ref>、<resultMap>。

  • <select>、<insert>、<update>、<delete>标签分别对应查询、添加、更改、删除操作

  • CRUD中的属性

  • parameterType:参数类型

    1. 基本数据类型,通过id查询User

      public User findById(long id);
      
      <select id="findById" parameterType="java.lang.Long"resultType="com.sheep.pojo.User">
          select * from user3 where id=#{id}
      </select>
      
      public static void main(String[] args) {
              InputStream resource=Test2.class.getClassLoader().getResourceAsStream("config.XML");
              SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
              SqlSessionFactory build=sqlSessionFactoryBuilder.build(resource);
              SqlSession sqlSession=build.openSession();
          	//获取接口代理对象
      		UserIntf mapper = sqlSession.getMapper(UserIntf.class);
      		User user = mapper.findById(2L);
              System.out.println(user);
              sqlSession.close();
      }
      
    2. String类型,通过name查询User

      public User findByName(String username);
      
      <select id="findByName" parameterType="java.lang.String" resultType="com.sheep.pojo.User">
              select * from user3 where username=#{username}
          </select>
      
      User user = mapper.findByName("内马尔");
      System.out.println(user);
      sqlSession.close();
      
    3. 包装类,通过id查询User

      public User findById2(long id);
      
      <select id="findById2" parameterType="java.lang.Long"resultType="com.sheep.pojo.User">
          select * from user3 where id=#{id}
      </select>
      
      User user = mapper.findById(2L);
      System.out.println(user);
      sqlSession.close();
      
    4. 多个参数

      public User findByNameAndAge(String username,int age);
      
      <select id="findByNameAndAge" resultType="com.sheep.pojo.User">
          select * from user3 where username=#{param1} and age=#{param2}
      	<!--有多个参数时不用写parameterType参数但取值时要使用#{param1}、#{param2}-->
      </select>
      
      User user = mapper.findByNameAndAge("C罗", 34);
      System.out.println(user);
      sqlSession.close();
      
    5. javaBean做参数

      public int update(User user);
      
      <update id="update" parameterType="com.sheep.pojo.User">
          update user3 set username=#{username},password=#{password},age=#{age} where id=#{id}
      </update>
      
      User user2 = mapper.findById(3L);
      user.setUsername("石磊");
      user.setPassword("666666");
      user.setAge(23);
      int update = mapper.update(user2);
      sqlSession.commit();
      sqlSession.cose();
      
    6. Map参数

      public User findUser(Map<Object, Object> map);
      
      <select id="findUser" parameterType="map" resultType="com.sheep.pojo.User">
          select * from user3 where id=#{abcdefg} and age=#{hijklmn}
      </select>
      
      Map<Object, Object> map = new HashMap<>();
      map.put("abcdefg",4);
      map.put("hijklmn",29);
      User user = mapper.findUser(map);
      System.out.println(user);
      sqlSession.close();
      

      当根据多个字段查询时使用Map集合更加方便。而且SQL语句中参数名只要与Map集合中的Key相同即可。

  • resultType:结果类型

    1. 基本数据类型

      public int count();
      
      <select id="count" resultType="int">
          select count(id) from user3
      </select>
      
      int count = mapper.count();
      System.out.println(count);
      sqlSession.close();
      
    2. 包装类

      public Integer count2();
      
      <select id="count2" resultType="java.lang.Integer">
          select count(id) from user3
      </select>
      
      Integer integer = mapper.count2();
      System.out.println(integer);
      sqlSession.close();
      
    3. String类型,通过id查询User的username

      public String findNameById(long id);
      
      <select id="findNameById" resultType="String">
          select username from user3 where id=#{id}
      </select>
      
      String name = mapper.findNameById(5L);
      System.out.println(name);
      sqlSession.close();
      
    4. JavaBean类型返回集合类型

      public List<User> findAll();
      
      <select id="findAll" resultType="com.sheep.pojo.User">
          select * from user3
      </select>
      
      List<User> all = mapper.findAll();
      for (User user:all){
          System.out.println(user);
      }
      sqlSession.close();
      
    5. javaBean类型

      public User findById(long id);
      
      <select id="findById" parameterType="java.lang.Long" resultType="com.sheep.pojo.User">
          select * from user3 where id=#{id}
      </select>
      
      User byId = mapper.findById(2L);
      System.out.println(byId);
      sqlSession.close();
      

Limit分页

引言

客服端传过来的start(页码),pageSize(每页显示的数据)两个参数去分页查询数据库,MySQL数据库提供了分页的函数limit m,n,但是该函数的用法和我们的需求不一样,我们要根据真实需求来写适合我i吗的分页语句。

例如:每页10条显示数据,

查询第1条SQL语句是:select * from table limit 0,10; = select * from table limit(1-1)*10,10;

查询第11条SQL语句是:select * from talbe limit 10,10; = select * from table limit(2-1)*10,10;

查询第21条SQL语句是:select * from table limit 20,10; = select * from table limit(3-1)*10,10;

limit(起始位,步长);

通过上面的分析,可以得出符合需求的分页sql格式:select * from table limit(start-1)*pageSize,pageSize;

简单案例

  1. 建表

     create table user3(
     id int primary key auto_increment,
     username varchar(11),
     password varchar(11),
     age int
     );
    
  2. 实体类

    public class User{
        private long id;
        private String username;
        private String password;
        private int age;
        ...请补充get、set
    }
    
  3. 创建接口

    public List<User> getUserByLimit(Map<Object, Object> map);
    
  4. mapper代理实现

    <select id="getUserByLimit" resultType="com.sheep.pojo.User">
        select * from user3 limit #{startIndex},#{pageSize}
    </select>
    
  5. 测试

    public static void main(String[] args) {
            InputStream resource = Test2.class.getClassLoader().getResourceAsStream("config.XML");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = sqlSessionFactoryBuilder.build(resource);
            SqlSession sqlSession = build.openSession();
    		Map<Object, Object> map = new HashMap<>();
            map.put("startIndex",0);
            map.put("pageSize",5);
            List<User> userByLimit = mapper.getUserByLimit(map);
            for (User user:userByLimit){
                System.out.println(user);
            }
    }
    
  6. 结果

    ==>  Preparing: select * from user3 limit ?,?
    ==> Parameters: 0(Integer), 4(Integer)
    <==    Columns: id, username, password, age
    <==        Row: 1, 梅西, 123123, 22
    <==        Row: 2, 梅西, 123123, 22
    <==        Row: 4, 内马尔, 1235679, 29
    <==        Row: 5, C罗, 1235679, 34
    <==      Total: 4
    user3{id=1, username='梅西', password='123123', age=22}
    user3{id=2, username='梅西', password='123123', age=22}
    user3{id=4, username='内马尔', password='1235679', age=29}
    user3{id=5, username='C罗', password='1235679', age=34}
    

级联操作

mybatis级联:通过数据库中的表可以描述数据之间的关系,在Java中,对象也可以进行关系描述

/**
*一对一
*/
Class A{			Class B{
    B b;				A a;
}					}
/**
*一对多
*/
Class A{			Class B{
    List<B> b;			A a;
}					}
/**
*多对多
*/
Class A{			Class B{
    List<B> b;			List<A> a;
}					}

一对一

  1. 创建表

    mysql> create table white(
        -> id int primary key auto_increment,
        -> name varchar(5)
        -> );
    mysql> create table black(
        -> id int primary key auto_increment,
        -> name varchar(10),
        -> wid int,
        -> foreign key(wid) references white(id)
        -> );
    
  2. 创建 表对应的实体类

    package com.sheep.pojo;
    
    public class Black{
        private int id;
        private String name;
        private White white;
    	...请补充get、set
    }
    
    package com.sheep.pojo;
    
    public class White {
        private int id;
        private String name;
        private Black black;
    	...请补充get、set
    }
    
  3. 创建接口

    package com.sheep.repository;
    import com.sheep.pojo.Black;
    
    public interface BlackRepository {
        public Black findById(long id);
    }
    
  4. 创建接口对应的代理对象

    <?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.sheep.repository.BlackRepository">
        <resultMap id="blackMap" type="com.sheep.pojo.Black">
            <result column="bname" property="name"/>
            <association property="white" javaType="com.sheep.pojo.White">
                <result column="wname" property="name"/>
            </association>
        </resultMap>
        <select id="findById" parameterType="long" resultMap="blackMap">
            select b.name bname,w.name wname from black b,white w where b.id=#{id} and b.wid=w.id;
        </select>
    </mapper>
    
  5. 在全局备注文件中注册代理对象

    <mapper resource="com/sheep/repository/BlackRepository.XML"/>
    
  6. 测试

    import com.sheep.pojo.Black;
    import com.sheep.repository.BlackRepository;
    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 resource = Test7.class.getClassLoader().getResourceAsStream("config.XML");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = sqlSessionFactoryBuilder.build(resource);
            SqlSession sqlSession = build.openSession();
            BlackRepository mapper = sqlSession.getMapper(BlackRepository.class);
            Black byId = mapper.findById(5l);
            System.out.println(byId);
        }
    }
    

    结果Black{id=0, name='z长空', white=White{id=0, name='大陆', black=null}}

一对多

  1. 创建表

    mysql> create table classes(
        -> id int primary key auto_increment,
        -> name varchar(10)
        -> );
    mysql> create table student(
        -> id int primary key auto_increment,
        -> name varchar(10),
        -> cid int,
        -> foreign key(cid) references classes(id)
        -> );
    
  2. 创建数据库对应的实体类

    Classes、班级

    package com.sheep.pojo;
    import java.util.List;
    
    public class Classes {
        private long id;
        private String name;
        private List<Student> studentList;
    	...请补充get、set
    }
    

    Student、班级

    package com.sheep.pojo;
    public class Student {
        private long id;
        private String name;
        private Classes classes;
    	...请补充get、set
    }
    
  3. 创建接口

    package com.sheep.repository;
    import com.sheep.pojo.Student;
    
    public interface StudentRepository {
        public Student findById(long id);
    }
    
    
  4. 创建代理类对应的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.sheep.repository.StudentRepository">
        <resultMap id="studentMap" type="com.sheep.pojo.Student">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
            <association property="classes" javaType="com.sheep.pojo.Classes">
                <id column="cid" property="id"/>
                <result column="cname" property="name"/>
            </association>
        </resultMap>
        <select id="findById" parameterType="long" resultMap="studentMap">
            select stu.id,stu.name,cla.id as cid,cla.name as cname from student stu,classes cla where stu.id=#{id} and stu.cid = cla.id
        </select>
    </mapper>
    

    xml中SQL语句

    select stu.id,stu.name,cla.id as cid,cla.name as cname 
    from student stu,classes cla 
    where stu.id=#{id} and stu.cid = cla.id
    < -- 根据stu、cla查找stu.id为#{id}并且stu.cid = cla.id的记录,-->
    < -- 并将该记录的stu.id、stu.name、cla.cid、cla.cname返回-- >
    

    xml中<resultMap>标签:

    <resultMap id="studentMap" type="com.sheep.pojo.Student">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <association property="classes" javaType="com.sheep.pojo.Classes">
            <id column="cid" property="id"/>
            <result column="cname" property="name"/>
        </association>
    </resultMap>
    
    • 在级联查询中SQL语句查询的结果,无法直接与实体类映射,需要使用<resultMap/>标签与查询结果间接映射
    • <resultMap id="间接映射id" type="代理接口的返回类型"/>
    • <id column="查询结果字段主键" property="d对应的实体类参数"/>
    • <result column="查询结果字段" property=对应的实体类参数""/>
    • <collection property="实体类集合变量" ofType="集合泛型"/>
  5. 在config.xml中注册

    <mapper resource="com/sheep/repository/StudentRepositoy.XML"/>
    
  6. 测试

    import com.sheep.pojo.Student;
    import com.sheep.repository.StudentRepository;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import java.io.InputStream;
    
    public class Test3 {
        public static void main(String[] args) {
            InputStream resource = Test2.class.getClassLoader().getResourceAsStream("config.XML");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = sqlSessionFactoryBuilder.build(resource);
            SqlSession sqlSession = build.openSession();
            StudentRepository mapper = sqlSession.getMapper(StudentRepository.class);
            Student student = mapper.findById(1L);
            System.out.println(student);
        }
    }
    

    结果Student{id=1, name='梅西', classes=Classes{id=1, name='z阿根廷', studentList=null}}

多对一

  1. 创建接口

    package com.sheep.repository;
    import com.sheep.pojo.Classes;
    
    public interface ClassesRepository {
        public Classes findById(long id);
    }
    
  2. 创建代理类对应的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.sheep.repository.ClassesRepository">
        <resultMap id="classesMap" type="com.sheep.pojo.Classes">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
            <collection property="studentList" ofType="com.sheep.pojo.Student">
                <id column="sid" property="id"/>
                <result column="sname" property="name"/>
            </collection>
        </resultMap>
        <select id="findById" parameterType="long" resultMap="classesMap">
            select c.id,c.name,s.id as sid,s.name as sname from classes c,student s where c.id=#{id} and c.id=s.cid;
        </select>
    </mapper>
    
    • 在级联查询中SQL语句查询的结果,无法直接与实体类映射,需要使用<resultMap/>标签与查询结果间接映射
    • <resultMap id="间接映射id" type="代理接口的返回类型"/>
    • <id column="查询结果字段主键" property="d对应的实体类参数"/>
    • <result column="查询结果字段" property=对应的实体类参数""/>
    • <collection property="实体类集合变量" ofType="集合泛型"/>
  3. 在config.xml中注册

    <mapper resource="com/sheep/repository/ClassesRepository.XML"/>
    
  4. import com.sheep.pojo.Classes;
    import com.sheep.repository.ClassesRepository;
    import com.sheep.repository.StudentRepository;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.InputStream;
    
    public class Test4 {
        public static void main(String[] args) {
            InputStream resource = Test2.class.getClassLoader().getResourceAsStream("config.XML");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = sqlSessionFactoryBuilder.build(resource);
            SqlSession sqlSession = build.openSession();
            ClassesRepository mapper = sqlSession.getMapper(ClassesRepository.class);
            Classes byId = mapper.findById(3l);
            System.out.println(byId);
        }
    }
    

    结果Classes{id=3, name='z巴西', studentList=[Student{id=6, name='内马尔', classes=null}, Student{id=7, name='小罗', classes=null}, Student{id=8, name='外星人', classes=null}, Student{id=9, name='贝利', classes=null}]}

多对多

  1. 创建三个表

    mysql> create table customer(
        -> id int primary key auto_increment,
        -> name varchar(10)
        -> );
    mysql> create table goods(
        -> id int primary key auto_increment,
        -> name varchar(15)
        -> );
    mysql> create table customer_goods(
        -> id int primary key auto_increment,
        -> cid int,
        -> gid int,
        -> foreign key(cid) references customer(id),
        -> foreign key(gid) references goods(id)
        -> );
    mysql> select * from customer;
    +----+--------+
    | id | name   |
    +----+--------+
    |  1 | 梅西   |
    |  2 | C罗    |
    |  3 | 詹姆斯 |
    |  4 | 张继科 |
    |  5 | 超级丹 |
    +----+--------+
    mysql> select * from customer_goods;
    +----+------+------+
    | id | cid  | gid  |
    +----+------+------+
    |  1 |    1 |    1 |
    |  2 |    2 |    1 |
    |  3 |    3 |    2 |
    |  4 |    4 |    3 |
    |  5 |    5 |    4 |
    |  6 |    1 |    4 |
    |  7 |    2 |    2 |
    |  8 |    4 |    1 |
    |  9 |    5 |    2 |
    | 10 |    2 |    4 |
    +----+------+------+
    mysql> select * from goods;
    +----+--------+
    | id | name   |
    +----+--------+
    |  1 | 足球   |
    |  2 | 篮球   |
    |  3 | 兵乓球 |
    |  4 | 羽毛球 |
    +----+--------+
    
  2. 创建表customer、goods对应的实体类

    package com.sheep.pojo;
    import java.util.List;
    
    public class Customer {
        private long id;
        private String name;
        private List<Goods> goodsList;
    	...请补充get、set
    }
    
    package com.sheep.pojo;
    import java.util.List;
    
    public class Goods {
        private long id;
        private String name;
        private List<Customer> customerList;
        public Goods(){}
    	...请补充get、set
    }
    
  3. 创建接口(客户对商品)

    package com.sheep.repository;
    import com.sheep.pojo.Customer;
    
    public interface CustomerRepository {
        public Customer findById(long id);
    }
    
  4. 新建接口的代理对象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.sheep.repository.CustomerRepository">
        <resultMap id="customerMap" type="com.sheep.pojo.Customer">
            <result column="cname" property="name"/>
            <collection property="goodsList" ofType="com.sheep.pojo.Goods">
                <result column="gname" property="name"/>
            </collection>
        </resultMap>
        <select id="findById" parameterType="long" resultMap="customerMap">
            select c.name cname,g.name gname from customer c,goods g,customer_goods cg where c.id=#{id} and c.id=cg.cid and g.id=cg.gid;
        </select>
    </mapper>
    
  5. 在config.xml中配置

    <mapper resource="com/sheep/repository/CustomerRepository.XML"/>
    
  6. 测试

    import com.sheep.pojo.Customer;
    import com.sheep.repository.CustomerRepository;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.InputStream;
    
    public class Test5 {
        public static void main(String[] args) {
            InputStream resource = Test2.class.getClassLoader().getResourceAsStream("config.XML");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = sqlSessionFactoryBuilder.build(resource);
            SqlSession sqlSession = build.openSession();
            CustomerRepository mapper = sqlSession.getMapper(CustomerRepository.class);
            Customer byId = mapper.findById(1l);
            System.out.println(byId);
        }
    }
    

    结果Customer{id=0, name='超级丹', goodsList=[Goods{id=0, name='羽毛球', customerList=null}, Goods{id=0, name='篮球', customerList=null}]}

  7. 创建接口(商品对客户)

    package com.sheep.repository;
    import com.sheep.pojo.Goods;
    
    public interface GoodsRepository {
        public Goods findById(long id);
    }
    
  8. 创建接口代理对象

    <?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.sheep.repository.GoodsRepository">
        <resultMap id="goodsMap" type="com.sheep.pojo.Goods">
            <result column="gname" property="name"/>
            <collection property="customerList" ofType="com.sheep.pojo.Customer">
                <result column="cname" property="name"/>
            </collection>
        </resultMap>
        <select id="findById" parameterType="long" resultMap="goodsMap">
            select g.name gname,c.name cname from customer c,goods g,customer_goods cg where g.id=#{id} and g.id=cg.cid and c.id=cg.gid;
        </select>
    </mapper>
    
  9. 在XML中注册

    <mapper resource="com/sheep/repository/GoodsRepository.XML"/>
    
  10. 测试

    import com.sheep.pojo.Goods;
    import com.sheep.repository.GoodsRepository;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import java.io.InputStream;
    
    public class Test6 {
        public static void main(String[] args) {
            InputStream resource = Test6.class.getClassLoader().getResourceAsStream("config.XML");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = sqlSessionFactoryBuilder.build(resource);
            SqlSession sqlSession = build.openSession();
            GoodsRepository mapper = sqlSession.getMapper(GoodsRepository.class);
            Goods byId = mapper.findById(1l);
            System.out.println(byId);
        }
    }
    

    结果Goods{id=0, name='足球', customerList=[Customer{id=0, name='梅西', goodsList=null}, Customer{id=0, name='张继科', goodsList=null}]}

总结

在级联查询中当返回的结果中有基本数据类型、List、实体类时。不能直接映射,只能使用间接映射。

当返回的结果中包含实体类时使用<resultMap>、<association>。

当返回的结果中包含List时使用<resultMap>、<collection>。


动态SQL

  • 创建表

    CREATE TABLE `trends` (
      `id` int NOT NULL AUTO_INCREMENT,
      `name` varchar(5) DEFAULT NULL,
      `password` varchar(10) DEFAULT NULL,
      `age` varchar(5) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
    
  • 创建表对应的实体类

    public class Trends {
        private int id;
        private String name;
        private String password;
        private String age;
        //ids变量用于<foreach/>
        private List<Long> ids;
    }
    

.<if>

  1. if

    public int sel(Trends trends);
    

    if标签:判断语句,相当于Java中的if,如果test="name !=null and name!='' 为 true 则执行该代码。否则不执行

    <!-- if语句 -->
        <select id="sel" parameterType="com.sheep.pojo.Trends" resultType="int">
            select * from trends where 1=1
            <if test="name !=null and name!=''">
                and name=#{name}
            </if>
            <if test="password != null and password != ''">
                and password=#{password}
            </if>
            <if test="age != null and age !=''">
                and age=#{age}
            </if>
        </select>
    
    /** if语句 */
            Trends trends = new Trends();
            trends.setName("梅西");
            trends.setPassword("666");
            trends.setAge("36");
            mapper.sel(trends);
    
  2. if+where

    public List<Trends> AllSelect(Trends trends);
    

    where标签:如果它包含的标签中有返回值,就插入一个where。如果标签返回的内容是and或or开头的则它会踢出。

    	<!-- if-where -->
        <!-- if-trim与if-where效果基本相等 -->
        <select id="AllSelect" parameterType="com.sheep.pojo.Trends" resultType="com.sheep.pojo.Trends">
            select * from trends
            <where>
                <if test="name != null and name != ''">
                    and name=#{name}
                </if>
                <if test="password != null and password != ''">
                    and password=#{password}
                </if>
            </where>
            <trim prefix="where" prefixOverrides="and | or">
                <if test="name != null and name != ''">
                    and name=#{name}
                </if>
                <if test="password != null and password != ''">
                    and password=#{password}
                </if>
            </trim>
    	</select>
    
    /** if-where 、if-trim*/
            Trends trends = new Trends();
            List<Trends> listTrends = mapper.AllSelect(trends);
            for (Trends trends2:listTrends){
                System.out.println(trends2);
            }
    
  3. if+trim

    public List<Trends> AllSelect(Trends trends);
    

    trim标签:是一个格式化的标记,可以完成set或者是where标记的功能

    prefix="where":如果有返回值就在返回值前面加where

    prefixOverrides="and | or":如果返回值开头是and | or就踢出

    	<!-- if-where -->
        <!-- if-trim与if-where效果基本相等 -->
        <select id="AllSelect" parameterType="com.sheep.pojo.Trends" resultType="com.sheep.pojo.Trends">
            select * from trends
            <trim prefix="where" prefixOverrides="and | or">
                <if test="name != null and name != ''">
                    and name=#{name}
                </if>
                <if test="password != null and password != ''">
                    and password=#{password}
                </if>
            </trim>
    	</select>
    
    /** if-where 、if-trim*/
            Trends trends = new Trends();
            List<Trends> listTrends = mapper.AllSelect(trends);
            for (Trends trends2:listTrends){
                System.out.println(trends2);
            }
    
  4. if+set

    public void update(Trends trends);
    

    set标签:专门用于更新操作,如果第一个条件为 false 则不执行该条件,继续判断下面语句。

    <!-- if-set...用于update语句 -->
        <select id="update" parameterType="com.sheep.pojo.Trends">
            update trends
            <set>
                <if test="name !=null and name !=''">
                    name=#{name},
                </if>
                <if test="age !=null and name !=''">
                    age=#{age}
                </if>
            </set>
            where id=#{id}
        </select>
    
     /** if-set */
            Trends trends = mapper.trendsAll(3);
            System.out.println(trends);
            trends.setName("小C罗");
            trends.setAge("18");
            mapper.update(trends);
            sqlSession.commit();
            sqlSession.close();
    

.<choose>

  1. public Trends findTrends(Trends trends);
    

    <choose>语句:类似于Java当中switch...case...default语句,如果when标签中test为true则赋值该SQL如果都不为真则最终执行otherwise标签中的代码。

    <!-- <choose><when><otherwise> -->
        <select id="findTrends" parameterType="com.sheep.pojo.Trends" resultType="com.sheep.pojo.Trends">
            select * from trends
            <where>
                <choose>
                    <when test="id != null and id != ''">
                        and id=#{id}
                    </when>
                    <when test="name != null and name !=''">
                        and name=#{name}
                    </when>
                    <when test="password !=null and password !=''">
                        and password=#{password}
                    </when>
                    <otherwise>
                        and age=#{age}
                    </otherwise>
                </choose>
            </where>
        </select>
    
     /**<choose><when><otherwise>*/
            Trends trends = new Trends();
            trends.setAge("18");
            Trends trends1 = mapper.findTrends(trends);
            System.out.println(trends1);
    

<where>、<trim>、<set>

  1. <where>:根据条件添加where,或踢出and | or
  2. <trim>:根据条件添加where,或踢出and | or
  3. <set>:用于更新操作(update)根据条件添加set。

.<foreach>

  1. public List<Trends> findByAllId(Trends trends);
    

    在实体类中添加属性ids

    //ids变量用于<foreach/>
    private List<Long> ids;
    

    foreach标签:当sql语句为:select * from user where id=1 or id=2 or id=3; || select * from user where id in (1,2,3);

    就可以使用foreach标签类似于Java中的for循环

    collection="ids":被循环的变量(array、list、或collection、Map集合的键、POJO包装类中的数组或集合类型的属性名)。

    open="id in (":以什么符号开头(相当于从or语句开始)

    separator=",":各个元素的间隔符

    close=")":以什么符号结尾

    item="id":将容器变量赋值给该属性并将该属性与sql语句中的属性关联。

    <select id="findByAllId" parameterType="com.sheep.pojo.Trends" resultType="com.sheep.pojo.Trends">
            select * from trends
            <where>
                <foreach collection="ids" item="id" open="id in (" separator="," close=")">
                    #{id}
                </foreach>
            </where>
        </select>
    
    /**<foreach>*/
    Trends trends1 = new Trends();
            List<Long> list = new ArrayList<>();
            list.add(1L);
            list.add(2L);
            list.add(3L);
            trends1.setIds(list);
            List<Trends> byAllId = mapper.findByAllId(trends1);
            for (Trends trends:byAllId){
                System.out.println(trends);
            }
    

SQL片段引用

  1. public int findAll(Trends trends);
    
    <!-- 引用sql片段 -->
    <!-- 被引用的是sql标签 -->
        <sql id="findAllTrends">
            <if test="name !=null and name !=''">
                and name=#{name}
            </if>
            <if test="password !=null and password != ''">
                and password=#{password}
            </if>
            <if test="age !=null and age !=''">
                and age=#{age}
            </if>
        </sql>
        <select id="findAll" parameterType="com.sheep.pojo.Trends" resultType="int">
            select * from trends
            <trim prefix="where" prefixOverrides="and | or">
                <include refid="findAllTrends"></include>
            </trim>
        </select>
    
    /** 引用sql片段 */
               Trends trends = new Trends();
               trends.setName("梅西");
               trends.setPassword("666");
               trends.setAge("36");
               int all = mapper.findAll(trends);
               System.out.println(all);
           }
    

浅聊MyBatis延迟加载

什么是延迟加载?

延迟加载其实就是将数据加载时机推迟,比如推迟嵌套查询的执行时机(延迟加载也称懒加载,就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据)。在Mybatis中经常用到关联查询,但是并不是任何时候都需要立即返回关联查询结果。比如查询用户信息,并不一定需要及时返回用户下对应的账户信息,再比如查询商品分类信息并不一定要及时返回该类别下有哪些产品,这种情况一下需要一种机制,当需要查看时,再执行查询,返回需要的结果集,这种需求在Mybatis中可以使用延迟加载机制来实现。延迟加载可以实现先查询主表,按需实时做关联查询,返回关联表结果集,一定程度上提高了效率

如何使用

不开启延迟加载

  1. 表classes、student

    mysql> create table classes(
        -> id int primary key auto_increment,
        -> name varchar(10)
        -> );
    mysql> create table student(
        -> id int primary key auto_increment,
        -> name varchar(10),
        -> cid int,
        -> foreign key(cid) references classes(id)
        -> );
    
  2. 表对应的实体类Student、Classes

    public class Student {
        private long id;
        private String name;
        private Classes classes;
        ...请补充get、set
    }
    
    public class Classes {
        private long id;
        private String name;
        private List<Student> studentList;
        ...请补充get、set
    }
    
  3. 创建接口ClassesRepository、StudentRepository

    package com.sheep.repository;
    import com.sheep.pojo.Classes;
    
    public interface ClassesRepository {
        public Classes findByIdLazy(long id);
    }
    
    
    package com.sheep.repository;
    import com.sheep.pojo.Student;
    
    public interface StudentRepository {
        public Student findById2(long id);
    }
    
    
  4. 创建接口的代理对象ClassesRepository.XML、StudentRepository.XML

    <?xml version="1.0" encoding="UTF8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!-- ClassesRepository.XML -->
    <mapper namespace="com.sheep.repository.ClassesRepository">
        <select id="findByIdLazy" parameterType="long" resultType="com.sheep.pojo.Classes">
            select * from classes where id=#{id}
        </select>
    </mapper>
    
    <?xml version="1.0" encoding="UTF8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!-- StudentRepository.XML -->
    <mapper namespace="com.sheep.repository.StudentRepository">
        <resultMap id="studentMap2" type="com.sheep.pojo.Student">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
            <association property="classes" javaType="com.sheep.pojo.Classes" select="com.sheep.repository.ClassesRepository.findByIdLazy" column="cid">
                <!-- 把当前结果集的cid当作条件当作com.sheep.repository.ClassesRepository.findByIdLazy方法的参数查询并返回结果 -->
            </association>
        </resultMap>
        <select id="findById2" parameterType="long" resultMap="studentMap2">
            select * from student where id=#{id}
        </select>
    </mapper>
    
  5. 测试

    import com.sheep.pojo.Student;
    import com.sheep.repository.StudentRepository;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import java.io.InputStream;
    
    public class Test3 {
        public static void main(String[] args) {
            InputStream resource = Test2.class.getClassLoader().getResourceAsStream("config.XML");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = sqlSessionFactoryBuilder.build(resource);
            SqlSession sqlSession = build.openSession();
            //通过StudentRepository运行时类创建对象
            StudentRepository mapper = sqlSession.getMapper(StudentRepository.class);
            Student student2 = mapper.findById2(2L);
            System.out.println(student2.getName());
        }
    }
    

    运行

    ==>  Preparing: select * from student where id=?
    ==> Parameters: 2(Long)
    <==    Columns: id, name, cid
    <==        Row: 2, 保罗.迪巴拉, 1
    ====>  Preparing: select * from classes where id=?
    ====> Parameters: 1(Long)
    <====    Columns: id, name
    <====        Row: 1, z阿根廷
    <====      Total: 1
    <==      Total: 1
    保罗.迪巴拉
    
  6. 在config.xml配置开启延时加载

    <settings>
        <!-- 设置SQL打印 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!-- 开启延迟加载 -->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>
    

    运行

    ==>  Preparing: select * from student where id=?
    ==> Parameters: 2(Long)
    <==    Columns: id, name, cid
    <==        Row: 2, 保罗.迪巴拉, 1
    <==      Total: 1
    保罗.迪巴拉
    

    可以看出开启延时加载后只打印了一次SQL语句


Mybatis缓存

什么是MyBatis缓存

​ 在一个Web项目中,查询数据库中的操作算是一个非常常用的操作,但是有些数据会被经常性的查询,而每一次都去数据库中查询这些重复的数据,会很消耗数据库的资源,同时使得查询效率也很低,而 MyBatis 中就通过缓存技术来解决这样的问题,也就是说:将一些经常查询,并且不经常改变的,以及数据的正确对最后的结果影响不大的数据,放置在一个缓存容器中,当用户再次查询这些数据的时候,就不必再去数据库中查询,直接在缓存中提取就可以了。

MyBatis 提供了 一级缓存和二级缓存两种形式

  • 一级缓存:它是 SqlSession 级别的缓存,SqlSession 类的实例对象中提供了一个 HashMap 的结构,可以用于存储缓存数据,当我们再次查询同一数据的时候,MyBatis 会先去 SqlSession 中查询,有的话,就直接调用
  • 二级缓存:是Mapper 级别的缓存,也就是说,如果多个 SqlSession 类的实例,去操作同一个Mapper配置文件中的SQL,这些实例对象可以共用二级缓存

一级缓存

通过一个例子来说:

  • 第一次查询 id 为某个值的用户信息时,先去 SqlSesion 的一级缓存中去寻找,如果找到了,就直接用,如果没有找到就去数据库中去查,然后将查到的内容存到一级缓存区域
  • 但是,如果在下一次操作中,执行了 commit 操作,也就是执行了增删改的操作,一级缓存区域内的内容会被清空,这是为了保证缓存中的数据的有效性,避免脏读的产生
  • 一级缓存是默认开启的。

如何使用

  1. 创建表

     create table user(
     id int primary key auto_increment,
     username varchar(11),
     password varchar(11),
     age int
     );
    
  2. 创建表对应的实体类

    public class User {
        private long id;
        private String username;
        private String password;
        private int age;
        ...请补充get、set
    }
    
  3. 创建接口

    public interface UserIntf {
        public User findById(long id);
    }
    
  4. 创建接口的代理对象

    <mapper namespace="com.sheep.repository.UserIntf">
        <select id="findById" parameterType="java.lang.Long" resultType="com.sheep.pojo.User">
            select * from user3 where id=#{id}
        </select>
    </mapper>
    
  5. 测试

    import com.sheep.pojo.User;
    import com.sheep.repository.UserIntf;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.InputStream;
    
    public class Test8 {
        public static void main(String[] args) {
            InputStream resourceAsStream = Test8.class.getClassLoader().getResourceAsStream("config.XML");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
            SqlSession sqlSession = build.openSession();
            UserIntf mapper = sqlSession.getMapper(UserIntf.class);
            User byId = mapper.findById(1L);
            System.out.println(byId);
            User byId2 = mapper.findById(1L);
            System.out.println(byId2);
            System.out.println(byId.hashCode());
            System.out.println(byId2.hashCode());
        }
    }
    

    可以看出虽然查询了两次但是只执行了一次SQL,而且两次返回的内存地址是一致的。

  6. 当关闭第一次SqlSession时

    import com.sheep.pojo.User;
    import com.sheep.repository.UserIntf;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import java.io.InputStream;
    
    public class Test8 {
        public static void main(String[] args) {
            InputStream resourceAsStream = Test8.class.getClassLoader().getResourceAsStream("config.XML");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
            SqlSession sqlSession = build.openSession();
            UserIntf mapper = sqlSession.getMapper(UserIntf.class);
            User byId = mapper.findById(1L);
            System.out.println(byId);
            System.out.println(byId.hashCode());
            //关闭session
            sqlSession.close();
            //从新开启
            SqlSession sqlSession1 = build.openSession();
            UserIntf mapper1 = sqlSession1.getMapper(UserIntf.class);
            User byId1 = mapper1.findById(1l);
            System.out.println(byId1);
            System.out.println(byId1.hashCode());
            sqlSession.close();
        }
    }
    

    当第一次关闭SqlSession后,返回的内存地址不相同,并且执行了两次SQL

二级缓存

一级缓存是基于同一个SqlSession的,但有时候由于方法的封装等原因、或者是在查询完后SqlSession对象会关闭,一级缓存就清空了,导致无法从中获取内容。

二级缓存可以解决一级缓存无法使用的情况,前面已经说过二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象加载同一个Mapper配置文件,并执行其中SQL配置,他们就共享同一个Mapper缓存。

  • 查询时,先去Mapper缓存区去找这个值,如果找不到,就去数据库查,然后将查询到的结果存储到缓存中,等下次使用
  • 当某个SqlSession类的实例对象执行了增删改操作时,二级缓存就会被清空。

如何使用

  1. 在config.xml中开启二级缓存

    <settings>
        <!-- 开启二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    
  2. 在Mapper映射文件中添加配置

    <cache/>
    
  3. 实体类实现序列化接口

    public class User implements Serializable {
        private long id;
        private String username;
        private String password;
        private int age;
    }    
    
  4. 测试

    import com.sheep.pojo.User;
    import com.sheep.repository.UserIntf;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import java.io.InputStream;
    
    public class Test8 {
        public static void main(String[] args) {
            InputStream resourceAsStream = Test8.class.getClassLoader().getResourceAsStream("config.XML");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
            SqlSession sqlSession = build.openSession();
            UserIntf mapper = sqlSession.getMapper(UserIntf.class);
            User byId = mapper.findById(1L);
            System.out.println(byId);
            System.out.println(byId.hashCode());
            //关闭session
            sqlSession.close();
            //从新开启
            SqlSession sqlSession1 = build.openSession();
            UserIntf mapper1 = sqlSession1.getMapper(UserIntf.class);
            User byId1 = mapper1.findById(1l);
            System.out.println(byId1);
            System.out.println(byId1.hashCode());
            sqlSession.close();
        }
    }
    

    可以看出第一次查询虽然关闭但是只查询了一次SQL语句,虽然两个对象地址并不相等。

MyBatis逆向工程

逆向工程

MyBatis框架需要:实体类、自定义Mapper接口、Mapper.xml

传统开发中上述的三个组件需要开发者手动创建,逆向工程可以帮助开发者自动创建三个组件,减轻开发者的工作量,提高效率。

如何使用

MyBatis Generator,简称MBG,是一个专门为MyBatis框架开发者定制的代码生成器,可自动生成MyBtais框架所需的实体类,Mapper接口、Mapper.xml,支持基本的CRUD操作。但是相对复杂的SQL需要开发者自己来完成。

  1. 新建Maven工程,pom.xml

    <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.22</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.6</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-core</artifactId>
                <version>1.4.0</version>
            </dependency>
        </dependencies>
        <!-- 解决找不到资源文件配置 -->
        <build>
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.XML</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>true</filtering>
                </resource>
            </resources>
        </build>
    
  2. 创建MBG配置文件generatorConfig.xml

    • jdbcConnection配置数据库连接信息
    • javaModelGenerator配置JavaBean生成策略
    • sqlMapGenerator配置SQL映射文件生成策略
    • javaClientGenerator配置Mapper接口的生成策略。
    • table配置目标数据表(tableName:表名,dmainObjectName:JavaBean类名)。
    <?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">
    <generatorConfiguration>
        <context id="testTables" targetRuntime="Mybatis3">
            <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                            connectionURL="jdbc:mysql://localhost:3306/user01?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true"
                            userId="root"
                            password="123321"
            ></jdbcConnection>
            <javaModelGenerator targetPackage="com.Sheep.pojo" targetProject="./src/main/java"></javaModelGenerator>
            <sqlMapGenerator targetPackage="com.Sheep.repository" targetProject="./src/main/java"></sqlMapGenerator>
            <javaClientGenerator type="XMLMAPPER" targetPackage="com.Sheep.repository" targetProject="./src/main/java"></javaClientGenerator>
            <table tableName="t_user" domainObjectName="User"></table>
        </context>
    </generatorConfiguration>
    
    <generatorConfiguration>
        <context id="自定义" targetRuntime="必须Mybatis3">
            <jdbcConnection driverClass="驱动"
                            connectionURL="URL"
                            userId="root"
                            password="password"
            ></jdbcConnection>
            <javaModelGenerator targetPackage="创建后的实体类存放的包" targetProject="包放的目录下"></javaModelGenerator>
            <sqlMapGenerator targetPackage="创建后的SQL映射存放的包" targetProject="./src/main/java"></sqlMapGenerator>
            <javaClientGenerator type="XMLMAPPER" targetPackage="com.Sheep.repository" targetProject="./src/main/java"></javaClientGenerator>
            <table tableName="要映射的表" domainObjectName="实体类额名字"></table>
        </context>
    </generatorConfiguration>
    
  3. 创建Generator执行的类

    package com.Sheep.test;
    import org.mybatis.generator.api.MyBatisGenerator;
    import org.mybatis.generator.config.Configuration;
    import org.mybatis.generator.config.xml.ConfigurationParser;
    import org.mybatis.generator.exception.InvalidConfigurationException;
    import org.mybatis.generator.exception.XMLParserException;
    import org.mybatis.generator.internal.DefaultShellCallback;
    
    import java.io.File;
    import java.io.IOException;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    public class Main {
        public static void main(String[] args) {
            List<String> warings = new ArrayList<String>();
            boolean overwrite = true;
            /**
            *genCig:resources/generatorConfig.xml
            */
            String genCig = "/generatorConfig.xml";
            File configfile = new File(Main.class.getResource(genCig).getFile());
            ConfigurationParser configurationParser = new ConfigurationParser(warings);
            Configuration configuration = null;
            try {
                configuration = configurationParser.parseConfiguration(configfile);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (XMLParserException e) {
                e.printStackTrace();
            }
            DefaultShellCallback callback = new DefaultShellCallback(overwrite);
            MyBatisGenerator myBatisGenerator = null;
            try {
                myBatisGenerator=new MyBatisGenerator(configuration,callback,warings);
            } catch (InvalidConfigurationException e) {
                e.printStackTrace();
            }
            try {
                myBatisGenerator.generate(null);
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
  4. 运行

posted @ 2021-03-31 21:27  一程山水一年华^_^  阅读(97)  评论(0)    收藏  举报
TOP 底部 /*显示代码块行号*/