Java框架-01-MyBatis

1. MyBatis简介

  1. MyBatis提供了数据库查询的自动对象绑定功能;SQL写在XML中,便于统一管理和优化,解除了SQL与代码的耦合;执行效率比Hibernate快;对于复杂SQL,使用MyBatis会更加灵活。但关联的表或字段较多时,SQL工作量很大;而且SQL依赖于数据库,导致数据库移植性较差。
  2. Hibernate是全自动,Hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成SQL;对于单表的CRUD,使用Hibernate开发更加高效。但学习和精通的门槛较高,而且由于是自动生成SQL,因此无法直接维护SQL。
  3. MyBatis官网:https://mybatis.org/mybatis-3/
  4. MyBatis作用:
    1. 减少JDBC的重复代码
    2. 使数据库环境信息的硬编码和执行的SQL语句的硬编码解耦
  5. MyBatis分页插件PageHelper网址:https://pagehelper.github.io/

2. MyBatis基本使用

以查询user表中所有user为例

2.1 创建Maven工程,添加坐标


    
    
    
        junit
        junit
        4.12
        test
    
    
    
        org.projectlombok
        lombok
        1.18.10
        provided
    
    
    
        mysql
        mysql-connector-java
        5.1.47
    
    
    
        org.mybatis
        mybatis
        3.5.3
    

2.2 创建POJO

-- 创建user表
CREATE TABLE user(
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT "id",
    username VARCHAR(255) NOT NULL DEFAULT "" COMMENT "用户名",
    password VARCHAR(16) NOT NULL DEFAULT "123456" COMMENT "密码",
    balance DOUBLE(16, 3) NOT NULL DEFAULT 0.0 COMMENT "余额"
)ENGINE=INNODB, CHARSET=utf8, COMMENT="用户表";

-- 添加用户
INSERT INTO user VALUES
    (NULL, 'user1', 'password1', 1000),
    (NULL, 'user2', 'password2', 2000),
    (NULL, 'user3', 'password3', 3000);


@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private Integer id;
    private String username;
    private String password;
    private Double balance;
}

2.3 创建PojoDao接口

原则:MyBatis的所有PojoDao接口,方法参数只设置一个;

由于是全类名+方法名的保存和寻找策略,因此PojoDao接口里的方法不能重载

PojoDao接口的工作原理是JDK动态代理

public interface UserDao {
    void addUser(User user);

    void deleteById(Integer id);

    void updateUser(User user);

    List findAll();
}

2.4 创建外部jdbc配置文件

在resources下创建jdbc.properties文件:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db01?useSSL=false
jdbc.username=root
jdbc.password=123456

2.5 创建MyBatis核心配置文件*

  1. 存储在resources根路径下
  2. 文件名随意(如:SqlMapConfig.xml)
  3. 需要引入约束

核心配置文件的顺序:

  1. properties:引入外部properties文件

  2. settings:全局配置参数

  3. typeAliases:给类型设置别名

    核心配置文件:typeAliases定义单个别名
    
        
    
    核心配置文件:typeAliases批量定义别名
    
        
    
    
    修改PojoDao.xml:
    
    
  4. typeHandlers:类型处理器

  5. objectFactory:对象工厂

  6. objectWrapperFactory

  7. reflectorFactory

  8. plugins:插件

  9. environments:环境配置

  10. databaseIdProvider:数据库厂商标识

  11. mappers:映射器




    
    
    
    
        
    
    
    
        
        
            
            
            
            
                
                
                
                
                
            
        

        
            
            
                
                
                
                
            
        
    

    
    
        
        
        

        
        
        
        
        
    

2.6 创建SQL映射文件*

  1. 存储在resources里面
  2. 全路径和文件名要和相应的PojoDao接口的相同(在resources里创建多级文件夹时用/分隔,而不是.)
  3. 需要引入约束

根标签mapper的namespace属性的值为对应的PojoDao接口的全限定名

根标签mapper下的每一个子标签对应PojoDao接口中的一个方法:

  1. 查询方法对应select标签
  2. 添加方法对应insert标签
  3. 删除方法对应delete标签
  4. 修改方法对应update标签

子标签的id对应方法的名字

子标签的parameterType对应方法的形参类型

子标签的resultType(只有select标签有)对应方法的返回值类型:

  1. 如果返回值类型是简单类型,则直接写对应的Java类型
  2. 如果返回值类型是POJO、Map,则直接写POJO的全限定名
  3. 如果返回值类型是Collection,则resultType是集合中元素的类型
  • 结果集的字段名和POJO的属性名能够对应时,resultType才可以自动映射

使用参数中的数据:

  1. 如果参数为pojo、map,则通过#{属性名或Map的key}来引入对应的值
  2. 如果参数为基本类型数据,则通过#{任意字符串}来引入对应的值
  3. 如果参数为pojo包含pojo,如ResultBean中包含User,则使用#{user.username}

resultMap标签:解决查询出来的结果的列名和JavaBean属性不一致的情况

  1. id属性:映射规则的唯一id
  2. type属性:将SQL查询出来的结果手动映射到哪个类



    
        
        
        
        
        
        
    
    

    
    
        
            select LAST_INSERT_ID()
        
        insert into user values (null, #{username}, #{password}, #{balance})
    

    
        update user set username = #{username}, password = #{password}, balance = #{balance}
    

    
        delete from user where id = #{id}
    

2.7 单元测试

public class UserDaoTest {
    InputStream is;
    SqlSession sqlSession;
    UserDao userDao;

    @Before
    public void init() throws IOException {
        // 1. 创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 2. 读取核心配置文件,转换成字节输入流
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 3. 创建SqlSessionFactory对象
        SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(is);
        // 4. 创建SqlSession对象
        sqlSession = sessionFactory.openSession();
        // 5. 创建UserDao的代理对象
        userDao = sqlSession.getMapper(UserDao.class);
    }

    @Test
    public void testFindAll() {
        // 6. 调用需要执行的方法
        List userList = userDao.findAll();
        for (User user : userList) {
            System.out.println(user);
        }
    }

    @Test
    public void testAddUser() {
        User user = new User(0, "user4", "654321", 4000.0);
        userDao.addUser(user);
        System.out.println(user.getId());
    }

    @After
    public void destroy() throws IOException {
        // 在CUD操作中,需要通过sqlSession.commit()方法来执行提交操作
        sqlSession.commit();
        // 7. 释放资源
        sqlSession.close();
        is.close();
    }
}

2.8 #{}与${}的区别*

  1. 传参的区别:
    1. #{}先用?占位,然后再设置参数,可以防止SQL注入
    2. ${}直接拼接SQL语句
  2. #{}不能写在引号里,而${}必须要写在引号里
  3. pojo、map类型的参数,#{}和${}括号里都是写属性名或Map的key
  4. 基本类型、字符串类型的参数,#{}和${}括号里可以写任意字符串
  • 模糊查询:
    1. "%"#{username}"%"
    2. concat("%", #{username}, "%")
    3. "%${value}%"

2.9 自定义SqlSessionFactory工具类

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class SqlSessionFactoryUtils {
    private static InputStream is;
    private static SqlSessionFactory sessionFactory;

    static {
        try {
            // 1. 创建SqlSessionFactoryBuilder对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            // 2. 读取核心配置文件,转换成字节输入流
            is = Resources.getResourceAsStream("SqlMapConfig.xml");
            // 3. 创建SqlSessionFactory对象
            sessionFactory = sqlSessionFactoryBuilder.build(is);
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取一个SqlSession
     */
    public static SqlSession openSession() {
        SqlSession sqlSession = null;
        // 4. 创建SqlSession对象
        sqlSession = sessionFactory.openSession();
        return sqlSession;
    }

    /**
     * 提交事务并关闭资源
     */
    public static void commitAndClose(SqlSession sqlSession) {
        sqlSession.commit();
        sqlSession.close();
    }

    /**
     * 回滚事务并关闭资源
     */
    public static void rollbackAndClose(SqlSession sqlSession) {
        sqlSession.rollback();
        sqlSession.close();
    }
}

3. MyBatis日志

  1. 导入坐标:

    
    
        log4j
        log4j
        1.2.12
    
    
        org.slf4j
        slf4j-api
        1.6.6
    
    
        org.slf4j
        slf4j-log4j12
        1.6.6
    
    
  2. 在resources下创建log4j.properties文件:

    #日志级别:ERROR > WARN > INFO > DEBUG
    log4j.rootLogger=DEBUG,stdout
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=[%-5p] %t %l %d %rms:%m%n
    log4j.appender.file=org.apache.log4j.FileAppender
    log4j.appender.file.File=E:\\Logs\\IdeaLogs\\IdeaLogs.log
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS\} %-5p [%t] {%c}-%m%n
    

4. Mybatis动态SQL*

动态SQL:SQL语句的主体结构,在编译时无法确定,只有等到程序运行后,在执行过程中才能确定

4.1 if标签

满足test属性的逻辑表达式,则if标签里面的语句有效

多条件查询时,每次查询的条件不确定有哪些,就可以使用if标签

当参数值为int的0时,MyBatis会将参数值其转化为空字符串

select * from user


    where balance = #{balance}

4.2 where标签

select * from user


    
        and balance = #{balance}
    
    
        and password = #{password}
    

4.3 foreach标签


delete from user


    #{id}

4.4 sql标签和include标签

sql标签可以定义公共的sql片段,需要时可以用include标签引入sql片段


    select * from user


5. Mybatis多表关联查询*

建立数据库环境:

DROP TABLE IF EXISTS user;
-- 创建用户表
CREATE TABLE user(
    uid INT PRIMARY KEY AUTO_INCREMENT COMMENT "id",
    username VARCHAR(255) NOT NULL DEFAULT "" COMMENT "用户名",
    password VARCHAR(16) NOT NULL DEFAULT "123456" COMMENT "密码",
    balance DOUBLE(16, 3) NOT NULL DEFAULT 0.0 COMMENT "余额"
)ENGINE=INNODB, CHARSET=utf8, COMMENT="用户表";
-- 添加用户
INSERT INTO user VALUES
    (NULL, 'user1', 'password1', 1000),
    (NULL, 'user2', 'password2', 2000),
    (NULL, 'user3', 'password3', 3000);

-- 创建账户表
CREATE TABLE account (
    aid INT AUTO_INCREMENT PRIMARY KEY,
    money DOUBLE,
    uid INT,
    CONSTRAINT fk_account_uid FOREIGN KEY (uid) REFERENCES user(uid)
)ENGINE=INNODB, CHARSET=utf8;
-- 添加账户
INSERT INTO account VALUES
    (NULL, 1000, 1),
    (NULL, 2000, 1),
    (NULL, 1000, 2),
    (NULL, 2000, 2),
    (NULL, 3000, 3);

-- 创建课程表
CREATE TABLE course(
    cid INT PRIMARY KEY AUTO_INCREMENT,
    coursename VARCHAR(255),
    book VARCHAR(255)
)ENGINE=INNODB, CHARSET=utf8;
-- 添加课程
INSERT INTO course VALUES
    (NULL, '面向对象程序设计', 'Java编程思想'),
    (NULL, '数据结构与算法', '算法导论'),
    (NULL, '设计模式', '大话设计模式');

-- 创建选课表
CREATE TABLE course_selection(
    uid INT,
    cid INT,
    CONSTRAINT fk_course_selection_uid FOREIGN KEY (uid) REFERENCES user(uid),
    CONSTRAINT fk_course_selection_cid FOREIGN KEY (cid) REFERENCES course(cid),
    PRIMARY KEY (uid, cid)
)ENGINE=INNODB, CHARSET=utf8;
-- 添加选课信息
INSERT INTO course_selection VALUES
    (1, 1),
    (1, 2),
    (1, 3),
    (2, 1),
    (2, 2),
    (3, 1);

5.1 一对一

根据账户id查询账户信息,并关联查询对应的用户表信息;由于一个账户只能对应一个用户,因此为一对一查询

在外键所在的POJO中新增一个外键参考的POJO类型的属性

  1. 创建Account表的POJO:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Account implements Serializable {
        private Integer aid;
        private Double money;
        private Integer uid;
        /**
         * 表示一个Account对应一个User
         */
        private User user;
    }
    
  2. 创建PojoDao接口:

    public interface AccountDao {
        Account findAccountUserByAid(int aid);
    }
    
  3. 创建SQL映射文件:

    
    
    
        
        
            
            
            
            
            
                
                
                
                
                
            
        
        
    
    
  4. 单元测试:

    @Test
    public void test01() {
        SqlSession sqlSession = SqlSessionFactoryUtils.openSession();
        AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
        Account account = accountDao.findAccountUserByAid(1);
        System.out.println(account);
        /**
         * 结果:
         * Account(aid=1, money=1000.0, uid=1, user=User(id=null, uid=1, username=user1, password=password1, balance=1000.0))
         */
    }
    

5.2 一对多

根据用户id查询用户信息,并关联查询对应的所有账户表信息;由于一个用户可能对应多个账户,因此为一对多查询

在外键参考的POJO中新增一个List<外键所在POJO>类型的属性

  1. 创建User表的POJO:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User implements Serializable {
        private Integer uid;
        private String username;
        private String password;
        private Double balance;
        /**
         * 表示一个User对应多个Account
         */
        private List accountList;
    }
    
  2. 创建PojoDao接口:

    public interface UserDao {
        User findUserAccountByUid(int uid);
    }
    
  3. 创建SQL映射文件:

    
    
    
        
        
            
            
            
            
            
            
                
                
                
                
            
        
        
    
    
  4. 单元测试:

    @Test
    public void test01() {
        SqlSession sqlSession = SqlSessionFactoryUtils.openSession();
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        User user = userDao.findUserAccountByUid(1);
        System.out.println(user);
        /**
         * 结果:
         * User(uid=1, username=user1, password=password1, balance=1000.0, 
         * accountList=[Account(aid=1, money=1000.0, uid=1, user=null), Account(aid=2, money=2000.0, uid=1, user=null)])
         */
    }
    

5.3 多对多

根据用户id查询用户所选的所有课程信息,相当于一个用户对应多个课程

多对多关系可以看成是双向的一对多关系

  1. 创建Course表的POJO:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Course implements Serializable {
        private Integer cid;
        private String coursename;
        private String book;
    }
    
  2. 创建User表的POJO:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User implements Serializable {
        private Integer uid;
        private String username;
        private String password;
        private Double balance;
        /**
         * 表示一个User对应多个Course
         */
        private List courseList;
    }
    
  3. 创建PojoDao接口:

    public interface UserDao {
        User findUserCourseByUid(int uid);
    }
    
  4. 创建SQL映射文件:

    
    
    
        
        
            
            
            
            
            
                
                
                
            
        
        
    
    
  5. 单元测试:

    @Test
    public void test01() {
        SqlSession sqlSession = SqlSessionFactoryUtils.openSession();
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        User user = userDao.findUserCourseByUid(2);
        System.out.println(user);
        /**
         * 结果:
         * User(uid=2, username=user2, password=password2, balance=2000.0,
         * courseList=[Course(cid=1, coursename=面向对象程序设计, book=Java编程思想), Course(cid=2, coursename=数据结构与算法, book=算法导论)])
         */
    }
    

6. Mybatis分步查询延迟加载*

  1. 立即加载:一调用方法就立即发起查询
  2. 延迟加载(懒加载):需要使用数据时,才发起查询;如果暂时不需要使用数据,则暂时不查询
    1. 缺点:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降
    2. 优点:先从单表查询,需要时再从关联表去关联查询;由于查询单表要比关联查询多张表速度要快,因此可以提高数据库性能
  3. 延迟加载的SQL语句类似于嵌套子查询,分步骤查询
  • 全局延迟加载:在MyBatis核心配置文件中配置

    
        
        
        
        
    
    

6.1 association标签实现分步查询延迟加载

根据账户id查询账户信息,在需要用户信息时再关联查询对应的用户表信息

  1. 创建User表和Account表的POJO:

    @NoArgsConstructor
    @AllArgsConstructor
    public class User implements Serializable {
        private Integer uid;
        private String username;
        private String password;
        private Double balance;
    }
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Account implements Serializable {
        private Integer aid;
        private Double money;
        private Integer uid;
        /**
         * 表示一个Account对应一个User
         */
        private User user;
    }
    
  2. 创建PojoDao接口:

    public interface AccountDao {
        Account findAccountByAid(int aid);
    }
    
    public interface UserDao {
        User findUserByUid(int uid);
    }
    
  3. 创建SQL映射文件AccountDao.xml:

    
    
    
        
            
            
            
            
            
            
        
        
    
    
  4. 创建SQL映射文件UserDao.xml:

    
    
    
        
    
    
  5. 单元测试:

    @Test
    public void test01() {
        SqlSession sqlSession = SqlSessionFactoryUtils.openSession();
        AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
        Account account = accountDao.findAccountByAid(1);
        System.out.println(account.getMoney());
        System.out.println(account.getUser());
    }
    

6.2 collection标签实现分步查询延迟加载

根据用户id查询用户信息,在需要账户信息时再关联查询对应的所有账户表信息

  1. 创建User表和Account表的POJO:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User implements Serializable {
        private Integer uid;
        private String username;
        private String password;
        private Double balance;
        /**
         * 表示一个User对应多个Account
         */
        private List accountList;
    }
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Account implements Serializable {
        private Integer aid;
        private Double money;
        private Integer uid;
    }
    
  2. 创建PojoDao接口:

    public interface UserDao {
        User findUserByUid(int uid);
    }
    
    public interface AccountDao {
        List findAccountListByUid(int uid);
    }
    
  3. 创建SQL映射文件UserDao.xml:

    
    
    
        
            
            
            
            
            
            
            
        
        
    
    
  4. 创建SQL映射文件AccountDao.xml:

    
    
    
        
    
    
  5. 单元测试:

    @Test
    public void test01() {
        SqlSession sqlSession = SqlSessionFactoryUtils.openSession();
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        User user = userDao.findUserByUid(1);
        System.out.println(user.getUsername());
        System.out.println(user.getAccountList());
    }
    

7. MyBatis缓存-

MyBatis缓存类别:

  1. 一级缓存:是sqlSession对象的缓存,自带的(不需要配置)不可卸载的(不想使用还不行);一级缓存的生命周期与sqlSession一致
  2. 二级缓存:是SqlSessionFactory的缓存,只要是同一个SqlSessionFactory创建的SqlSession就共享二级缓存的内容,并且可以操作二级缓存。二级缓存如果要使用,需要手动开启(需要配置)

7.1 一级缓存

  1. 查询时,先去缓存中找;如果缓存中没有,则从数据库查询用户信息,然后将查询到的信息存储到缓存中;如果缓存中有,则直接从缓存中获取信息。
  2. 清除一级缓存:
    1. sqlSession.commit(); // commit时清除一级缓存是为了避免数据发生增删改后产生脏读
    2. sqlSession.close();
    3. sqlSession.clearCache();
    4. 执行增删改操作

7.2 二级缓存

  1. 步骤1:在MyBatis核心配置文件中开启二级缓存(由于默认开启,因此这一步可省略)

    
    
        
    
    
  2. 步骤2:在要使用二级缓存的SQL映射文件的mapper标签里增加cache标签

    
        
        
    
    
  3. 步骤3:要使用二级缓存的POJO类必须实现Serializable接口

  • 只有当前的sqlSession.close()时,该sqlSession的数据才会存入二级缓存中
  • 执行增删改操作时会清除二级缓存
  • select标签属性:
    • flushCache默认为false
    • useCache默认为true
  • insert、delete、update标签属性:
    • flushCache默认为true
    • 没有useCache属性

8. MyBatis注解-

使用注解代替了SQL映射文件

@SelectKey():代替了selectKey标签
@Insert("SQL"):代替了insert标签
@Delete("SQL"):代替了delete标签
@Update("SQL"):代替了update标签
@Select("SQL"):代替了select标签

@Results(id = "", value = @Result())
或@Results(@Result())
或@Results({@Result(), @Result()})
@Result(column = "列名", property = "属性名", one = @One(select = "指定用来多表查询的sqlmapper"), many = @Many(select = ""))
    @Results代替了resultMap标签
    @Result代替了result标签和id标签
    @One()代替了association标签
    @Many()代替了collection标签

8.1 MyBatis注解实现基本CRUD

  1. 创建PojoDao接口:

    public interface UserDao {
        @SelectKey(keyProperty = "uid", keyColumn = "uid", before = false, statement = "select last_insert_id()", resultType = int.class)
        @Insert("INSERT INTO user VALUES (NULL, #{username}, #{password}, #{balance})")
        void addUser(User user);
    
        @Delete("delete from user where uid = #{id}")
        void deleteById(Integer id);
    
        @Update("update user set username = #{username}, password = #{password}, balance = #{balance} where uid = #{uid}")
        void updateUser(User user);
    
        @Select("select * from user")
        List findAll();
    }
    
  2. 创建MyBatis核心配置文件

  3. 单元测试:

    @Test
    public void test01() {
        SqlSession sqlSession = SqlSessionFactoryUtils.openSession();
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        User user = new User(2, "user4", "password4", 4000.0);
        userDao.addUser(user);
        System.out.println(user.getUid());
        userDao.deleteById(3);
        userDao.updateUser(user);
        userDao.findAll().stream().forEach(System.out::println);
        SqlSessionFactoryUtils.commitAndClose(sqlSession);
    }
    

8.2 MyBatis注解实现一对一映射及延迟加载

  1. 创建Account表的POJO:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Account implements Serializable {
        private Integer aid;
        private Double money;
        private Integer uid;
        /**
         * 表示一个Account对应一个User
         */
        private User user;
    }
    
  2. 创建PojoDao接口:

    public interface UserDao {
        @Select("select * from user where uid = #{id}")
        User findUserByUid(int uid);
    }
    
    public interface AccountDao {
        @Results(id = "accountUserMap", value = {
                @Result(column = "uid", property = "uid"),
                @Result(property = "user", one = @One(select = "com.liu2m.dao.UserDao.findUserByUid", fetchType = FetchType.LAZY), column = "uid")
        })
        @Select("select * from account where aid = #{aid}")
        Account findAccountByAid(int aid);
    }
    
  3. 单元测试:

    @Test
    public void test01() {
        SqlSession sqlSession = SqlSessionFactoryUtils.openSession();
        AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
        Account account = accountDao.findAccountByAid(1);
        System.out.println(account.getMoney());
    }
    

8.3 MyBatis注解实现一对多映射及延迟加载

  1. 创建User表的POJO:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User implements Serializable {
        private Integer uid;
        private String username;
        private String password;
        private Double balance;
        /**
         * 表示一个User对应多个Account
         */
        private List accountList;
    }
    
  2. 创建PojoDao接口:

    public interface UserDao {
        @Results(id = "userAccountMap", value = {
                @Result(column = "uid", property = "uid", id = true),
                @Result(property = "accountList", many = @Many(select = "com.liu2m.dao.AccountDao.findAccountListByUid", fetchType = FetchType.LAZY), column = "uid")
        })
        @Select("SELECT * FROM user WHERE uid = #{uid}")
        User findUserByUid(int uid);
    }
    
    public interface AccountDao {
        @Select("SELECT * FROM account WHERE uid = #{uid}")
        List findAccountListByUid(int uid);
    }
    
  3. 单元测试:

    @Test
    public void test01() {
        SqlSession sqlSession = SqlSessionFactoryUtils.openSession();
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        User user = userDao.findUserByUid(1);
        System.out.println(user);
    }
    

你可能感兴趣的:(Java框架-01-MyBatis)