Mybatis一:理论知识和增删改查


目录
  • 1 概述
    • 1.1 简介
    • 1.2 核心对象
    • 1.3 mybatis配置文件
    • 1.4 mybatis映射文件
  • 2 入门程序
    • 2.1 xml方式
    • 2.2 annotation方式
  • 3 增删改查
    • 3.1 xml方式
    • 3.2 annotation方式

1 概述

1.1 简介

  1. mybatis是个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注SQL语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
  2. mybatis通过xml或annotation的方式将要执行的各种statement配置起来,并通过java对象和statement中SQL的动态参数进行映射生成最终执行的SQL语句,最后由mybatis框架执行SQL并将结果映射为java对象并返回。
  3. 采用ORM(Object Relational Mappging对象关系映射)思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。

1.2 核心对象

  • 在使用MyBatis框架时,主要涉及两个核心对象:SqlSessionFactory和SqlSession,它们在MyBatis框架中起着至关重要的作用,图解如下,下面进行详解。

  1. SqlSessionFactory

    1. SqlSessionFactory是单个数据库映射关系经过编译后的内存镜像,主要作用是创建SqlSession。

    2. SqlSessionFactory是线程安全的,一旦被创建整个应用执行期间都会存在。如果多次的创建同一个数据库的SqlSessionFactory,那么数据库资源很容易被耗尽,因此在构建SqlSessionFactory实例时,一般使用单例模式。

      // 创建SqlSessionFactory代码
      InputStream inputStream = Resources.getResourceAsStream("配置文件位置");
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      
  2. SqlSession

    1. SqlSession是应用程序与持久层之间执行交互操作的一个单线程对象,是线程不安全的。SqlSession使用完后,要及时关闭它。

    2. SqlSession方法如下(了解即可,IDEA中可以点击ctrl+鼠标左键查看)

      // 注:若使用mybatis的传统开发方式(即编写接口的实现类)需要用到以下方法,但不推荐使用此方式,推荐用xml或annotation
      public interface SqlSession extends Closeable {
      
          // statement是在配置文件中,
              select * from t_user
      
      

2 入门程序

-- SQL脚本如下
CREATE TABLE `t_user` (
  `id` int(11) NOT NULL auto_increment,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` datetime default NULL COMMENT '生日',
  `sex` char(1) default NULL COMMENT '性别',
  `address` varchar(256) default NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `t_user`(`id`,`username`,`birthday`,`sex`,`address`) 
values  (41,'张三','2018-02-27 17:47:08','男','北京'),
	(42,'小明','2018-03-02 15:09:37','女','北京金燕龙'),
	(43,'小明','2018-03-04 11:34:34','女','北京金燕龙'),
	(45,'李四','2018-03-04 12:04:06','男','北京金燕龙'),
	(46,'王五','2018-03-07 17:37:26','男','北京'),
	(48,'赵六','2018-03-08 11:44:00','女','北京修正');

2.1 xml方式

  • 普通maven工程,结构图如下

  1. 依赖

    
        org.mybatis
        mybatis
        3.5.5
    
    
        mysql
        mysql-connector-java
        8.0.21
    
    
        log4j
        log4j
        1.2.17
    
    
        org.projectlombok
        lombok
        1.18.22
    
    
  2. 实体类

    package cn.pojo;
    import lombok.ToString;
    import java.util.Date;
    
    @ToString
    public class User {
        private Integer id;
        private String username;
        private Date birthday;
        private String six;
        private String address;
    }
    
  3. 持久层

    package cn.dao;
    import cn.pojo.User;
    import java.util.List;
    
    public interface UserDao {
        List findAll();
        /**
         * 注:在实际发开中,都是越简单越好,故不采用写Dao实现类的方式,使用xml或annotation都行。
         * 虽然不推荐写Dao实现类,但是mybatis是支持写实现类的。
         */
    }
    
  4. 配置

    # resources/log4j.properties,把下面内容直接粘贴过去即可
    
    # Set root category priority to INFO and its only appender to CONSOLE.
    #log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
    log4j.rootCategory=debug, CONSOLE, LOGFILE
    # Set the enterprise logger category to FATAL and its only appender to CONSOLE.
    log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
    # CONSOLE is set to be a ConsoleAppender using a PatternLayout.
    log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
    log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
    log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
    # LOGFILE is set to be a File appender using a PatternLayout.
    log4j.appender.LOGFILE=org.apache.log4j.FileAppender
    log4j.appender.LOGFILE.File=d:\axis.log
    log4j.appender.LOGFILE.Append=true
    log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
    log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
    
    
    
    
    <?xml version="1.0" encoding="UTF-8"?>
    
    
        
        
    
    
    
    
    <?xml version="1.0" encoding="UTF-8"?>
    
    
    
        
        
            
            
                
                
                
                
                    
                    
                    
                    
                    
                
            
        
        
        
            
        
    
    
  5. 运行

    package cn;
    
    import cn.dao.UserDao;
    import cn.pojo.User;
    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;
    import java.util.List;
    
    public class App {
        public static void main(String[] args) throws IOException {
            //1.读取配置文件
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            //2.创建SqlSessionFactory工厂
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
            //3.使用工厂生产SqlSession对象
            SqlSession session = factory.openSession();
            //4.使用SqlSession创建Dao接口的代理对象
            UserDao userDao = session.getMapper(UserDao.class);
            //5.使用代理对象执行方法
            List users = userDao.findAll();
            for (User user : users) {
                System.out.println(user);
            }
            //6.释放资源
            session.close();
            inputStream.close();
        }
        /**
         * 结果
         * User(id=41, username=张三, birthday=Tue Feb 27 17:47:08 CST 2018, six=null, address=北京)
         * User(id=42, username=小明, birthday=Fri Mar 02 15:09:37 CST 2018, six=null, address=北京金燕龙)
         * User(id=43, username=小明, birthday=Sun Mar 04 11:34:34 CST 2018, six=null, address=北京金燕龙)
         * User(id=45, username=李四, birthday=Sun Mar 04 12:04:06 CST 2018, six=null, address=北京金燕龙)
         * User(id=46, username=王五, birthday=Wed Mar 07 17:37:26 CST 2018, six=null, address=北京)
         * User(id=48, username=赵六, birthday=Thu Mar 08 11:44:00 CST 2018, six=null, address=北京修正)
         */
    }
    

2.2 annotation方式

  • 普通maven工程,结构图如下

  1. 依赖

    
        org.mybatis
        mybatis
        3.5.5
    
    
        mysql
        mysql-connector-java
        8.0.21
    
    
        log4j
        log4j
        1.2.17
    
    
        junit
        junit
        4.12
    
    
        org.projectlombok
        lombok
        1.18.22
    
    
  2. 实体类

    • 同 xml 方式实体类
  3. 持久层

    package cn.dao;
    import cn.pojo.User;
    import org.apache.ibatis.annotations.Select;
    
    import java.util.List;
    
    public interface UserDao {
        @Select("select * from t_user")
        List findAll();
    }
    
  4. 配置

    1. log4j.properties 日志配置同上面 xml 方式,略

    2. mybatis-config.xml 配置如下

      <?xml version="1.0" encoding="UTF-8"?>
      
      
          
              
                  
                  
                      
                      
                      
                      
                  
              
          
          
              
              
          
      
      
  5. 测试

    package cn.dao;
    import cn.pojo.User;
    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 org.junit.Test;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    
    public class UserDaoTest {
    
        @Test
        public void findAll() throws IOException {
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
            SqlSession session = factory.openSession();
            UserDao userDao = session.getMapper(UserDao.class);
            List users = userDao.findAll();
            for (User user : users) {
                System.out.println(user);
            }
            session.close();
            inputStream.close();
        }
    
        /**
         * 结果
         * User(id=41, username=张三, birthday=Tue Feb 27 17:47:08 CST 2018, six=null, address=北京)
         * User(id=42, username=小明, birthday=Fri Mar 02 15:09:37 CST 2018, six=null, address=北京金燕龙)
         * User(id=43, username=小明, birthday=Sun Mar 04 11:34:34 CST 2018, six=null, address=北京金燕龙)
         * User(id=45, username=李四, birthday=Sun Mar 04 12:04:06 CST 2018, six=null, address=北京金燕龙)
         * User(id=46, username=王五, birthday=Wed Mar 07 17:37:26 CST 2018, six=null, address=北京)
         * User(id=48, username=赵六, birthday=Thu Mar 08 11:44:00 CST 2018, six=null, address=北京修正)
         */
    }
    

3 增删改查

  1. SQL脚本同上面的入门程序,略

  2. #{}${} 的对比

    #{} ${}
    一个占位符 一个sql串
    自动进行java类型和jdbc类型转换 不进行jdbc类型转换
    防止sql注入 无法防止sql注入
    可以接收简单类型值或pojo属性值,
    如果parameterType传输单个简单类型值,
    #{}括号中可以是value或其它名称。
    可以接收简单类型值或pojo属性值,
    如果parameterType传输单个简单类型值,
    ${}括号中只能是value
    1. #{}相当于JDBC中的"?",而 "#{id}"表示该占位符待接收参数的名称为id。
    2. 拼接sql串时若想防止sql注入,可用mysql中的concat()函数。eg. select * from t_customer where username like concat('%',#{value},'%')

3.1 xml方式

  • 普通maven工程,结构图如下

  • 整个项目基于上面入门程序的xml方式改造,因此下文仅展示不同部分的代码,相同部分略

  1. 实体类

    package cn.pojo;
    import lombok.Data;
    import java.util.Date;
    
    @Data
    public class User {
        // 特意加个前缀user于列名不同,主要是用于测试resultMap映射
        private Integer userId;
        private String userName;
        private String userAddress;
        private String userSex;
        private Date userBirthday;
    }
    
    package cn.pojo;
    import lombok.Data;
    
    @Data
    public class FindPojo {
        User user;
    }
    
  2. 持久层

    package cn.mapper;
    import cn.pojo.FindPojo;
    import cn.pojo.User;
    import java.util.List;
    
    /**
     * mapper层即持久层,与以前的dao层一样。
     * 也可以写dao.UserDao,配置文件写成UserDao.xml
     * 但是mybatis提供的是mapper标签,因此用mapper居多,
     * 也可以写成dao层,两者完全一样
     */
    public interface UserMapper {
        void addUser(User user);
    
        void deleteUser(Integer id);
    
        void updateUser(User user);
    
        //包装类参数(注意xml中文件参数写法)
        List findByPojo(FindPojo pojo);
    
        List findAll();
    }
    
  3. 配置

    <?xml version="1.0" encoding="UTF-8"?>
    
    
        
            INSERT INTO t_user (username, birthday, sex, address)
            VALUES (#{userName}, #{userBirthday}, #{userSex}, #{userAddress})
        
    
        
        
        
            DELETE
            FROM t_user
            WHERE id = #{id}
        
    
        
            UPDATE t_user
            SET username = #{userName},
                address  = #{userAddress},
                sex      = #{userSex},
                birthday = #{userBirthday}
            WHERE id = #{userId}
        
    
        
        
        
        
    
        
    
        
            
            
            
            
            
            
            
        
    
    
  4. 测试

    package cn.mapper;
    
    import cn.pojo.FindPojo;
    import cn.pojo.User;
    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 org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Date;
    import java.util.List;
    
    public class UserMapperTest {
    
        /**
         * 注:增删改提交事务的方法都放在 @After public void destory()方法中
         * 别没看全以为不用提交事务!
         */
        private InputStream inputStream;
        private SqlSession sqlSession;
        private UserMapper userMapper;
    
        @Before
        public void init() throws IOException {
            inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = factory.openSession();
            userMapper = sqlSession.getMapper(UserMapper.class);
        }
    
        @After
        public void destory() throws IOException {
            //释放资源前提交事务,写在destory()内可以减少代码冗余,免得都写
            sqlSession.commit();
            //6.释放资源
            sqlSession.close();
            inputStream.close();
        }
    
        @Test
        public void addUser() {
            User user = new User();
            user.setUserName("张良");
            user.setUserBirthday(new Date());
            user.setUserSex("男");
            user.setUserAddress("郑州");
            userMapper.addUser(user);
        }
    
        @Test
        public void deleteUser() {
            userMapper.deleteUser(48);
        }
    
        @Test
        public void updateUser() {
            User user = new User();
            user.setUserName("孙悟空");
            user.setUserBirthday(new Date());
            user.setUserSex("男");
            user.setUserAddress("水帘洞");
            user.setUserId(46);
            userMapper.updateUser(user);
        }
    
        @Test
        public void findByPojo() {
            FindPojo pojo = new FindPojo();
            User user = new User();
            user.setUserName("%小%");
            pojo.setUser(user);
            List users = userMapper.findByPojo(pojo);
            System.out.println(users);
        }
    
        @Test
        public void findAll() {
            List users = userMapper.findAll();
            for (User user : users) {
                System.out.println(user);
            }
        }
    }
    

3.2 annotation方式

常用注解

序号 注解 说明
1 @Insert
2 @Delete
3 @Update
4 @Select
5 @Result 实现结果集封装
6 @Results 可以与@Result一起使用,封装多个结果集
7 @ResultMap 实现引用@Results定义的封装
8 @One 实现一对一结果集封装
9 @Many 实现一对多结果集封装
10 @SelectProvider 实现动态SQL映射
11 @CacheNamespace 实现注解二级缓存的使用

复杂注解映射说明

  1. @Results注解代替的是标签,该注解可以使用单个@Result,也可以使用@Result集合。
    1. 样例: @Results(@Result()) 或 @Results({@Result(),@Result()})
  2. @Result注解代替的是标签,属性如下
    1. id : 是否是主键字段
    2. property : 需要装配的属性名
    3. column : 数据库的列名
    4. one : 需要使用@One注解 ( @Result(one=@One) )
    5. many : 需要使用@Many注解 ( @Result(many = @Many) )
  3. @One注解(一对一)代替的是标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
    1. 其select属性指定用来多表查询的sqlMapper。
    2. 样例: @Result(property="",column=" ",one=@One(select="")
  4. @Many注解(一对多)代替的是标签,是多表查询的关键,在注解中用来指定子查询返回对象集合。
    1. 注意:聚集元素用来处理“一对多”的关系。需要指定映射的Java实体类的属性,属性的javaType(一般为ArrayList)但是注解中可以不定义。
    2. 样例: @Result(property="",column="",many=@Many(select=""))

代码

  • 普通maven工程,结构图如下。

  • 整个项目基于上面增删改查xml方式改造,因此下文仅展示不同部分的代码,相同部分略

  1. 持久层

    package cn.mapper;
    import cn.pojo.FindPojo;
    import cn.pojo.User;
    import org.apache.ibatis.annotations.*;
    import java.util.List;
    
    /**
     * 个人建议:短sql用注解,太长的sql写在xml文件中,否则方法上一大堆太丑
     * 注:定义了@Results(id = "userMap")之后,以后可以用@ResultMap("userMap")进行引用,
     * 例如下面的findByPojo方法可以不写SQL别名,添加注解@ResultMap("userMap")即可
     * 定义@Results的方法(即findAll方法)无需使用,若是加上会编译报错!!!
     */
    public interface UserMapper {
    
        @Insert("INSERT INTO t_user (username, birthday, sex, address)" +
                "VALUES (#{userName}, #{userBirthday}, #{userSex}, #{userAddress})")
        void addUser(User user);
    
        @Delete("DELETE FROM t_user WHERE id = #{id}")
        void deleteUser(Integer id);
    
        @Update("UPDATE t_user  SET username = #{userName},  address = #{userAddress}," +
                "sex = #{userSex}, birthday = #{userBirthday}  WHERE id = #{userId}")
        void updateUser(User user);
    
    
        @Select("SELECT ID AS userId, USERNAME AS userName, BIRTHDAY AS userBirthday," +
                "SEX AS userSex, ADDRESS AS userAddress FROM t_user WHERE USERNAME LIKE #{user.userName}")
        List findByPojo(FindPojo pojo); // 包装类参数
    
        @Select("SELECT * FROM t_user")
        @Results(id = "userMap",
                value = {
                        @Result(property = "userId", column = "id"),
                        @Result(property = "userName", column = "username"),
                        @Result(property = "userAddress", column = "address"),
                        @Result(property = "userSex", column = "sex"),
                        @Result(property = "userBirthday", column = "birthday")
                }
        )
        List findAll();
    }
    
  2. 配置 mybatis-config.xml

    
        
    
    
  3. 测试

    • 同上面增删改查xml方式代码,略。
  • 源码链接:blogs-mybatis