Mybatis框架学习3 - 使用Mybatis框架实现CRUD(增删改查)

文章目录

  • 使用 Mybatis 完成 CRUD
  • 使用 Mybatis 完成 CRUD
    • 1. 使用 Mybatis 进行查询所有的操作
    • 2. 使用 Mybatis 进行插入数据的操作
    • 3.使用 Mybatis 进行更新的操作
    • 4.使用 Mybatis 进行删除的操作
    • 5.使用 Mybatis 进行查询一个操作
    • 6. 使用 Mybatis 框架进行模糊查询操作
      • 6.1 查询的过程
      • 6.2 #{} 和 ¥{} 的区别
    • 7. 使用 Mybatis 进行聚合查询
      • 7.1 使用聚合查询查询结果
      • 7.2 获取保存数据的id

使用 Mybatis 完成 CRUD

本节包括:

  • curd
  • 参数深入及结果集的深入
  • 基于传统的 dao 的方式 (编写 dao 实现类) – 了解内容

使用 Mybatis 完成 CRUD

本节包括:

  • curd
  • 参数深入及结果集的深入
  • 基于传统的 dao 的方式 (编写 dao 实现类) – 了解内容
  • 主配置文件 - properties 标签 - typeAliases 标签 - mappers 标签

1. 使用 Mybatis 进行查询所有的操作

  1. 创建一个 maven 项目,不适用任何骨架

  2. 向 maven 项目的 pom.xml 中添加一下依赖项:

    
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
    
        <groupId>com.itheimagroupId>
        <artifactId>maven_mybatis_curdartifactId>
        <version>1.0-SNAPSHOTversion>
        <packaging>jarpackaging>
    
        <dependencies>
            <dependency>
                <groupId>org.mybatisgroupId>
                <artifactId>mybatisartifactId>
                <version>3.5.2version>
            dependency>
    
            <dependency>
                <groupId>junitgroupId>
                <artifactId>junitartifactId>
                <version>4.12version>
                <scope>testscope>
            dependency>
    
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
                <version>8.0.17version>
            dependency>
    
            <dependency>
                <groupId>log4jgroupId>
                <artifactId>log4jartifactId>
                <version>1.2.17version>
            dependency>
    
        dependencies>
    project>
    
  3. 然后,写实体类和dao方法类

    实体类:

    package com.itheima.domain;
    
    import java.io.Serializable;
    import java.util.Date;
    
    public class User implements Serializable {
        private Integer id;
        private String username;
        private String address;
        private String sex;
        private Date birthday;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", address='" + address + '\'' +
                    ", sex='" + sex + '\'' +
                    ", birthday=" + birthday +
                    '}';
        }
    }
    

    dao 接口类:

    package com.itheima.dao;
    
    import com.itheima.domain.User;
    
    import java.util.List;
    
    /**
     * 用户持久层
     */
    public interface IUserDao {
        /**
         * 查询所有用户
         * @return List
         */
        List<User> findAll();
    }
    
  4. 写完 dao 接口类 和 实体类之后,在 resources 文件下面配置 Mybatis 的主配置文件(这个名字是任意的),里面存放了连接数据库的信息,和 mysql语句 映射文件的位置

    
    
    <configuration>
    
        <environments default="mysql">
    
            <environment id="mysql">
    
                <transactionManager type="JDBC">transactionManager>
    
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/day23?serverTimezone=Asia/Shanghai"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                dataSource>
            environment>
        environments>
    
        <mappers>
            <mapper resource="com/itheima/dao/IUserDao.xml">mapper>
        mappers>
    configuration>
    
  5. 之后,因为配置了映射文件的位置,根据 Mybatis 的要求,我们需要在 resources 资源文件下创建一个和 dao 接口文件目录结构相同的 xml 文件,这个 xml 文件用来存放我们需要使用的 sql 语句(这个 xml 文件的名字也是任意的,只要自己分的清楚就可以了)

    
    
    
    <mapper namespace="com.itheima.dao.IUserDao">
    
    
    
        <select id="findAll" resultType="com.itheima.domain.User">
            select * from user;
        select>
    mapper>
    
  6. 对了,因为我们使用的是 log4j 来记录日志信息的,所以千万别忘记了在 resources 文件下面创建一个 log4j 的属性文件。

    # 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
    
  7. 然后,就可以通过测试类来查询 Mybatis 数据库了。

    package com.itheima.test;
    
    import com.itheima.dao.IUserDao;
    import com.itheima.domain.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;
    
    /**
     * 测试 Mybatis 的 CRUD 方法
     */
    public class MybatisTest {
    
        @Test
        public void testFindAll() throws IOException {
            // 1. 读取配置文件,生成文件字符流
            InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
            // 2. 获取 SqlSessionFactory
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
            // 3. 获取 SqlSession 对象
            SqlSession sqlSession = factory.openSession();
            // 4. 获取 dao 的代理对象
            IUserDao userDao = sqlSession.getMapper(IUserDao.class);
            // 5. 执行查询方法
            List<User> users = userDao.findAll();
            for(User user : users) {
                System.out.println(user);
            }
            // 6. 释放资源
            sqlSession.close();
            is.close();
        }
    
    }
    
  8. 最后,是运行的结果:

    2019-08-21 19:28:08,788 0      [           main] DEBUG ache.ibatis.logging.LogFactory  - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
    2019-08-21 19:28:08,818 30     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 19:28:08,818 30     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 19:28:08,819 31     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 19:28:08,819 31     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 19:28:08,875 87     [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
    2019-08-21 19:28:09,673 885    [           main] DEBUG source.pooled.PooledDataSource  - Created connection 1708169732.
    2019-08-21 19:28:09,674 886    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@65d09a04]
    2019-08-21 19:28:09,677 889    [           main] DEBUG m.itheima.dao.IUserDao.findAll  - ==>  Preparing: select * from user; 
    2019-08-21 19:28:09,707 919    [           main] DEBUG m.itheima.dao.IUserDao.findAll  - ==> Parameters: 
    2019-08-21 19:28:09,738 950    [           main] DEBUG m.itheima.dao.IUserDao.findAll  - <==      Total: 6
    User{id=41, username='老王', address='北京', sex='男', birthday=Tue Feb 27 17:47:08 CST 2018}
    User{id=42, username='小二王', address='北京金燕龙', sex='女', birthday=Fri Mar 02 15:09:37 CST 2018}
    User{id=43, username='小二王', address='北京金燕龙', sex='女', birthday=Sun Mar 04 11:34:34 CST 2018}
    User{id=45, username='传智播客', address='北京金燕龙', sex='男', birthday=Sun Mar 04 12:04:06 CST 2018}
    User{id=46, username='老王', address='北京', sex='男', birthday=Wed Mar 07 17:37:26 CST 2018}
    User{id=48, username='小马宝莉', address='北京修正', sex='女', birthday=Thu Mar 08 11:44:00 CST 2018}
    2019-08-21 19:28:09,740 952    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@65d09a04]
    2019-08-21 19:28:09,740 952    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@65d09a04]
    2019-08-21 19:28:09,740 952    [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 1708169732 to pool.
    

以上即为使用 Mybatis 框架查询所有数据的过程。

2. 使用 Mybatis 进行插入数据的操作

在我们写的查询所有数据的基础上面,我们进行一些添加,就可以完成插入数据的操作。

  1. 首先,我们需要进行插入操作,那么我们就应该修改 dao 文件为以下代码,添加了插入的方法:

    import com.itheima.domain.User;
    
    import java.util.List;
    
    /**
     * 用户持久层
     */
    public interface IUserDao {
        /**
         * 查询所有用户
         * @return List
         */
        List<User> findAll();
    
        /**
         * 保存用户
         * @param user
         */
        void saveUser(User user);
    }
    
  2. 然后,由于在 dao 类中添加了查询的方法,如果想要使用,我们必须在映射文件添加 SQL 语句,将 resources 文件夹下面,与 dao 接口相同文件结构的 xml文件,首先修改成以下这样:

    
    
    
    <mapper namespace="com.itheima.dao.IUserDao">
    
    
    
        <select id="findAll" resultType="com.itheima.domain.User">
            select * from user;
        select>
    
        <insert id="saveUser" parameterType="com.itheima.domain.User">
            insert into user (username, address, sex, birthday) values (#{});
        insert>
    mapper>
    

    我们需要按照 Mybatis 框架的要求,values 语句中提供数据的部分以 (#{属性名称}, #{属性名称}, #{属性名称}, #{属性名称}) 代替 (?,?,?,?)。

    首先,我们来看一下我们写的实体类:

    package com.itheima.domain;
    
    import java.io.Serializable;
    import java.util.Date;
    
    public class User implements Serializable {
        private Integer id;
        private String username;
        private String address;
        private String sex;
        private Date birthday;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", address='" + address + '\'' +
                    ", sex='" + sex + '\'' +
                    ", birthday=" + birthday +
                    '}';
        }
    }
    

    里面的类名是 id,username,address,birthday。这里因为我设置的 id 键是自增的,所以在插入的时候并不需要,因此在提供属性名的时候,并不需要 id 属性。

    这里需要注意的是,我这里使用的 intellij idea IDE,我们在写实体类的时候,如果你的 get and set 方法是 使用 alt + insert 自动生成的,那么在映射文件中直接填写类名即可,这种情况下,mybatis 自动通过传入参数,反射出来实体类中的属性名,拿到值。如果是自己手动写的,那么我们需要写get方法后面的名称。

    接下来,按照之前的分析,补全映射文件:

    
    
    
    <mapper namespace="com.itheima.dao.IUserDao">
    
    
    
        <select id="findAll" resultType="com.itheima.domain.User">
            select * from user;
        select>
    
        <insert id="saveUser" parameterType="com.itheima.domain.User">
            insert into user (username, address, sex, birthday) values (#{username}, #{address}, #{sex}, #{birthday});
        insert>
    mapper>
    

    这样写了之后,我们的属性名对应的数据便会被 Mybatis 取出来,组成插入数据库的这么一条 SQL 语句。

  3. 接着,我们就可以在我们的测试类中使用我们的插入数据的代码了:

    import com.itheima.dao.IUserDao;
    import com.itheima.domain.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;
    
    /**
     * 测试 Mybatis 的 CRUD 方法
     */
    public class MybatisTest {
    
        private InputStream is;
        private SqlSession sqlSession;
        private IUserDao userDao;
    
        // 由于需要频繁地读取配置文件数据,所以我们将读取配置文件数据和关闭连接两个方法封装起来,以供后面的调用。
        // 测试方法执行前
        @Before
        public void init() throws IOException {
            // 1. 读取配置文件,生成文件字符流
            is = Resources.getResourceAsStream("SqlMapConfig.xml");
            // 2. 获取 SqlSessionFactory
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
            // 3. 获取 SqlSession 对象
            sqlSession = factory.openSession();
            // 4. 获取 dao 的代理对象
            userDao = sqlSession.getMapper(IUserDao.class);
        }
    
        // 测试方法执行后
        @After
        public void destroy() throws IOException {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (is != null) {
                is.close();
            }
        }
    
        /**
         * 测试保存操作
         */
        @Test
        public void testSave() throws IOException {
            User user = new User();
            user.setUsername("mybatis save user");
            user.setAddress("China");
            user.setSex("男");
            user.setBirthday(new Date());
            // 5. 执行保存方法
            userDao.saveUser(user);
            // 6. 释放资源
    
        }
    
    
    }
    

    然后我们运行文件,会发现不会报错但是没有数据的写入,这是为什么呢?

    我们来看一下日志文件的记录:

    2019-08-21 21:09:40,111 1      [           main] DEBUG ache.ibatis.logging.LogFactory  - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
    2019-08-21 21:09:40,136 26     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 21:09:40,136 26     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 21:09:40,136 26     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 21:09:40,136 26     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 21:09:40,196 86     [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
    2019-08-21 21:09:41,025 915    [           main] DEBUG source.pooled.PooledDataSource  - Created connection 945722724.
    2019-08-21 21:09:41,025 915    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    2019-08-21 21:09:41,030 920    [           main] DEBUG .itheima.dao.IUserDao.saveUser  - ==>  Preparing: insert into user (username, address, sex, birthday) values (?, ?, ?, ?); 
    2019-08-21 21:09:41,066 956    [           main] DEBUG .itheima.dao.IUserDao.saveUser  - ==> Parameters: mybatis save user(String), China(String), 男(String), 2019-08-21 21:09:40.187(Timestamp)
    2019-08-21 21:09:41,069 959    [           main] DEBUG .itheima.dao.IUserDao.saveUser  - <==    Updates: 1
    2019-08-21 21:09:41,069 959    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Rolling back JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    2019-08-21 21:09:41,072 962    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    2019-08-21 21:09:41,072 962    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    2019-08-21 21:09:41,072 962    [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 945722724 to pool.
    

    其中有这么一段话:

    Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    

    这句话的意思是,设置自动提交数据库连接失败,需要手动去设置连接。但是由于我们接下来并没有手动地去设置连接,所以实际上数据库什么都没有干就回滚了操作,所以,我们的数据并没有写入。

    那么应该怎么办呢?

    这时候只需要在我们 testSave 方法最后添加这么一句话:

    // 提交事务
    sqlSession.commit();
    

    我们手动将事务提交,那么就可以实现插入数据了。

  4. 以下是运行成功时的结果:

    2019-08-21 21:14:13,732 0      [           main] DEBUG ache.ibatis.logging.LogFactory  - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
    2019-08-21 21:14:13,761 29     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 21:14:13,761 29     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 21:14:13,761 29     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 21:14:13,761 29     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 21:14:13,823 91     [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
    2019-08-21 21:14:14,658 926    [           main] DEBUG source.pooled.PooledDataSource  - Created connection 945722724.
    2019-08-21 21:14:14,658 926    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    2019-08-21 21:14:14,662 930    [           main] DEBUG .itheima.dao.IUserDao.saveUser  - ==>  Preparing: insert into user (username, address, sex, birthday) values (?, ?, ?, ?); 
    2019-08-21 21:14:14,698 966    [           main] DEBUG .itheima.dao.IUserDao.saveUser  - ==> Parameters: mybatis save user(String), China(String), 男(String), 2019-08-21 21:14:13.814(Timestamp)
    2019-08-21 21:14:14,701 969    [           main] DEBUG .itheima.dao.IUserDao.saveUser  - <==    Updates: 1
    2019-08-21 21:14:14,701 969    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    2019-08-21 21:14:14,703 971    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    2019-08-21 21:14:14,704 972    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    2019-08-21 21:14:14,704 972    [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 945722724 to pool.
    

    此时,数据库中:

    id, username, birthday, sex, address

    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, 女, 北京修正
    51, mybatis save user, 2019-08-21 21:07:56, 男, China
    53, mybatis save user, 2019-08-21 21:14:14, 男, China

    由于我为了实现写入了两次数据库,所以出现了两次相同的数据

3.使用 Mybatis 进行更新的操作

更新的操作具体上和插入数据类似.

  1. 首先,我们先在 dao 接口中,添加更新数据的操作。

    import com.itheima.domain.User;
    
    import java.util.List;
    
    /**
     * 用户持久层
     */
    public interface IUserDao {
        /**
         * 更新用户
         * @param user
         */
        void updateUser(User user);
    }
    
  2. 然后,在映射文件中,添加 SQL 语句

    
    
    
    <mapper namespace="com.itheima.dao.IUserDao">
    
    
        <update id="updateUser" parameterType="com.itheima.domain.User">
            update user set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id};
        update>
    mapper>
    
  3. 最后,回到测试类中,测试我们的更新操作:

    package com.itheima.test;
    
    import com.itheima.dao.IUserDao;
    import com.itheima.domain.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;
    
    /**
     * 测试 Mybatis 的 CRUD 方法
     */
    public class MybatisTest {
    
        private InputStream is;
        private SqlSession sqlSession;
        private IUserDao userDao;
    
        @Before
        public void init() throws IOException {
            // 1. 读取配置文件,生成文件字符流
            is = Resources.getResourceAsStream("SqlMapConfig.xml");
            // 2. 获取 SqlSessionFactory
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
            // 3. 获取 SqlSession 对象
            sqlSession = factory.openSession();
            // 4. 获取 dao 的代理对象
            userDao = sqlSession.getMapper(IUserDao.class);
        }
    
        @After
        public void destroy() throws IOException {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (is != null) {
                is.close();
            }
        }
    
        @Test
        public void testUpdate() {
            User user = new User();
            user.setId(51);
            user.setUsername("mybatis update user");
            user.setAddress("China");
            user.setSex("女");
    
            // 执行更新方法
            userDao.updateUser(user);
            sqlSession.commit();
        }
    
    }
    

    由于这里并没有写查询 id 的方法,所以我们需要手动地给其设置一个id。

  4. 接下来是运行结果:

    2019-08-21 23:19:39,926 0      [           main] DEBUG ache.ibatis.logging.LogFactory  - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
    2019-08-21 23:19:39,952 26     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 23:19:39,952 26     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 23:19:39,952 26     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 23:19:39,952 26     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-21 23:19:40,012 86     [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
    2019-08-21 23:19:40,777 851    [           main] DEBUG source.pooled.PooledDataSource  - Created connection 945722724.
    2019-08-21 23:19:40,778 852    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    2019-08-21 23:19:40,781 855    [           main] DEBUG theima.dao.IUserDao.updateUser  - ==>  Preparing: update user set username=?, address=?, sex=?, birthday=? where id=?; 
    2019-08-21 23:19:40,807 881    [           main] DEBUG theima.dao.IUserDao.updateUser  - ==> Parameters: mybatis update user(String), China(String), 女(String), 2019-08-21 23:19:40.003(Timestamp), 51(Integer)
    2019-08-21 23:19:40,809 883    [           main] DEBUG theima.dao.IUserDao.updateUser  - <==    Updates: 1
    2019-08-21 23:19:40,810 884    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    2019-08-21 23:19:40,813 887    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    2019-08-21 23:19:40,814 888    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@385e9564]
    2019-08-21 23:19:40,814 888    [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 945722724 to pool.
    
  5. 以下为数据库中的结果:

    id, username, birthday, sex, address

    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, 女, 北京修正
    51, mybatis update user, 2019-08-21 23:19:40, 女, China
    53, mybatis save user, 2019-08-21 21:14:14, 男, China

    我们发现,id=51 的数据已经得到了更新

4.使用 Mybatis 进行删除的操作

删除操作与更新数据操作、插入数据操作步骤类似

  1. 首先在 dao 接口中,写上删除的方法:

    package com.itheima.dao;
    
    import com.itheima.domain.User;
    
    import java.util.List;
    
    /**
     * 用户持久层
     */
    public interface IUserDao {
        /**
         * 根据 id 删除用户
         * @param id
         */
        void deleteUser(Integer id);
    }
    
  2. 然后,在映射文件中,写上需要执行的SQL语句

    
    
    
    <mapper namespace="com.itheima.dao.IUserDao">
    
    
        <delete id="deleteUser" parameterType="java.lang.Integer">
            delete from user where id = #{id};
        delete>
    mapper>
    

    这里需要注意下的是,由于我们在 deleteUser方法中传入的参数类型是 Integer,所以在 delete 标签中的 parameterType 应该设置为 Integer (int 和 java.lang.Integer)都可以。由于传入的参数仅仅只有一个 Integer 类型的 id,所以,在转化拼接成 SQL 语句时,并不会出现什么无法识别的错误,可以非常好的识别并进行 SQL 语句的转化。因此,#{ } 中这个 id 仅仅只是一个占位符,写什么都可以。

  3. 最后,在测试类中测试删除方法:

    package com.itheima.test;
    
    import com.itheima.dao.IUserDao;
    import com.itheima.domain.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;
    
    /**
     * 测试 Mybatis 的 CRUD 方法
     */
    public class MybatisTest {
    
        private InputStream is;
        private SqlSession sqlSession;
        private IUserDao userDao;
    
        @Before
        public void init() throws IOException {
            // 1. 读取配置文件,生成文件字符流
            is = Resources.getResourceAsStream("SqlMapConfig.xml");
            // 2. 获取 SqlSessionFactory
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
            // 3. 获取 SqlSession 对象
            sqlSession = factory.openSession();
            // 4. 获取 dao 的代理对象
            userDao = sqlSession.getMapper(IUserDao.class);
        }
    
        @After
        public void destroy() throws IOException {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (is != null) {
                is.close();
            }
        }
    
        @Test
        public void testDelete() {
            Integer id = 53;
    
            // 执行更新方法
            userDao.deleteUser(id);
            sqlSession.commit();
        }
    
    }
    
  4. 执行测试类,发现控制台没有报错。然后查看一下数据库,发现 id = 52 的元素已经消失

    id, username, birthday, sex, address

    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, 女, 北京修正
    51, mybatis update user, 2019-08-21 23:19:40, 女, China

5.使用 Mybatis 进行查询一个操作

进行一个查询的操作过程和以上的过程类似:

  1. 在 dao 接口中写入以下方法:

    package com.itheima.dao;
    
    import com.itheima.domain.User;
    
    import java.util.List;
    
    /**
     * 用户持久层
     */
    public interface IUserDao {
        /**
         * 根据用户的 id 查找用户
         * @param id
         * @return
         */
        User findById(Integer id);
    }
    
  2. 在映射文件中写入以下标签和方法:

    
    
    
    <mapper namespace="com.itheima.dao.IUserDao">
    
    
        <select id="findById" parameterType="int" resultType="com.itheima.domain.User">
            select * from user where id = #{id};
        select>
    mapper>
    

    这里我们需要注意一点的是,我们在填写好参数类型为“int”之后,我们会发现,我们这次返回的值是一个 User 对象,那么我们必须设置好结果集类型为 User,让 Mybatis 框架可以将查询出的结果封装到 User 对象中。

  3. 然后在测试类中写入以下测试方法:

    @Test
        public void testFindOne() {
            // 执行查询一个方法
            User user = userDao.findById(51);
            System.out.println(user);
            sqlSession.commit();
        }
    
  4. 运行,成功查询出结果:

    User{id=51, username='mybatis update user', address='China', sex='女', birthday=Wed Aug 21 23:19:40 CST 2019}
    

6. 使用 Mybatis 框架进行模糊查询操作

6.1 查询的过程

和以上的操作过程相似:

  1. 在 Dao 接口中,写入模糊查询的方法:

    /**
         * 根据名称,模糊查询用户信息
         * @param username
         * @return
         */
        List<User> findByName(String username);
    
  2. 在映射文件中写入以下标签:

    
        <select id="findByName" parameterType="String" resultType="com.itheima.domain.User">
            select * from user where username like #{name};
        select>
    

    这里注意,我们传入的参数类型是一个 String 类型,同时我们必须设置结果集类型,以便 Mybatis 框架实现结果集的封装。

  3. 在测试类中使用测试方法来测试正确与否:

    @Test
        public void testFindByName() {
            // 执行查询一个方法
            List<User> users = userDao.findByName("%王");
            for(User user : users) {
                System.out.println(user);
            }
            sqlSession.commit();
        }
    

    由于模糊查询需要使用 % 来进行查询所有包括 王 字的人。又因为我们在配置文件并没有添加 %,所以我们在传入参数时,必须手动加入。

  4. 运行之后,发现没有问题,同时查询出的结果如下:

    User{id=41, username='老王', address='北京', sex='男', birthday=Tue Feb 27 17:47:08 CST 2018}
    User{id=42, username='小二王', address='北京金燕龙', sex='女', birthday=Fri Mar 02 15:09:37 CST 2018}
    User{id=43, username='小二王', address='北京金燕龙', sex='女', birthday=Sun Mar 04 11:34:34 CST 2018}
    User{id=46, username='老王', address='北京', sex='男', birthday=Wed Mar 07 17:37:26 CST 2018}
    

**注意点:**模糊查询除了支持上述的方法以外,还支持以下的 SQL 的写法:

select * from user where username like '%${value}%';

但是,如果使用这种写法,那么意味着 大括号中的 value 只能写 value,是固定。

同时,不要忘了,我们需要修改测试类,删除传入的参数里面的 % (因为 SQL 语句中已经提供了):

@Test
    public void testFindByName() {
        // 执行查询一个方法
        List<User> users = userDao.findByName("王");
        for(User user : users) {
            System.out.println(user);
        }
        sqlSession.commit();
    }

6.2 #{} 和 ¥{} 的区别

#{} 表示一个占位符

​ 通过 #{} 可以实现 PreparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,#{} 可以有效防止 sql 注入。 #{} 可以接受简单类型值或 pojo 属性值。如果 parameterType 传输单个简单类型值,#{} 括号中可以是 value 或 其他名称。

${} 表示拼接 SQL 串

​ 通过 ¥{} 可以将 parameterType 传入的内容拼接在 SQL 中且不进行 jdbc 类型转换,¥{} 可以接受简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型指,¥{} 括号中只能是 value。

模糊查询的${value}的源码分析

​ 我们来一起看一下 TextSqlNode 类的源码:

@Override
public String handleToken (String content) {
    Object parameter = context.getBindings().get("_parameter");
    if(parameter == null) {
        context.getBindings().put("value", null);
    }else if(SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
        context.getBindings().put("value", parameter);
    }
    Object value = OgnCache.getValue(content, context.getBindings());
    String srtValue = (Value == null ?"" : String.valueOf(value));
    checkInjection(srtValue);
    return srtValue;
}

​ 这就说明了源码中制定了读取的 key 的名字就是“value”,所以我们在绑定参数是就只能叫 value 的名字了。

而在我们实际的开发中,一般推荐使用 #{},这时由于 #{} 使用的是向占位符中传入值,实现 java 类型和 jdbc 类型之间的转化,调用的预处理方法;而 ${} 使用的是字符串的拼接,谁具有优势不言而喻。

7. 使用 Mybatis 进行聚合查询

7.1 使用聚合查询查询结果

  1. 在 dao 接口中写入查询数据库中数据总条数的方法:

    /**
         * 查询总用户数
         * @return
         */
        int findTotal();
    
  2. 然后在映射文件写入以下配置:

    
        <select id="findTotal" resultType="int">
            select count(id) from user;
        select>
    
  3. 然后,在测试类中写入以下测试方法,并执行:

    /**
         * 测试查询总记录条数
         */
        @Test
        public void testFindTotal() {
            // 执行查询一个方法
            int total = userDao.findTotal();
            sqlSession.commit();
            System.out.println(total);
        }
    
  4. 以下是结果:

    2019-08-22 12:06:49,144 0      [           main] DEBUG ache.ibatis.logging.LogFactory  - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
    2019-08-22 12:06:49,178 34     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-22 12:06:49,178 34     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-22 12:06:49,178 34     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-22 12:06:49,178 34     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-22 12:06:49,255 111    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
    2019-08-22 12:06:50,337 1193   [           main] DEBUG source.pooled.PooledDataSource  - Created connection 63390.
    2019-08-22 12:06:50,337 1193   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f79e]
    2019-08-22 12:06:50,342 1198   [           main] DEBUG itheima.dao.IUserDao.findTotal  - ==>  Preparing: select count(id) from user; 
    2019-08-22 12:06:50,365 1221   [           main] DEBUG itheima.dao.IUserDao.findTotal  - ==> Parameters: 
    2019-08-22 12:06:50,389 1245   [           main] DEBUG itheima.dao.IUserDao.findTotal  - <==      Total: 1
    7
    2019-08-22 12:06:50,392 1248   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f79e]
    2019-08-22 12:06:50,392 1248   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f79e]
    2019-08-22 12:06:50,392 1248   [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 63390 to pool.7.2
    

7.2 获取保存数据的id

因为我们设置的数据库的 id 是自动增长的,所以就可以通过以下 sql 语句来获取自动增加的 id

select last_insert_id();

其过程只需要修改之前我们的写保存用户数据的配置信息:

  1. 配置一下映射文件,配置文件的内容在之前写的保存用户的配置文件中:

    <insert id="saveUser" parameterType="com.itheima.domain.User">
            
            <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
                select last_insert_id();
            selectKey>
            insert into user (username, address, sex, birthday) values (#{username}, #{address}, #{sex}, #{birthday});
        insert>
    

    其中,keyProperty 是我们需要查询的属性名称,keyColumn 是数据库中的列名,resultType 是我们需要返回的数据的类型,order 是在下面的主要的语句执行前还是执行后运行该SQL语句。

  2. 为了更加直观的看到我们已经取回了 id 的值,我们可以对测试类的中测试方法进行以下修改:

    @Test
        public void testSave() throws IOException {
            User user = new User();
            user.setUsername("mybatis save user and get id");
            user.setAddress("China");
            user.setSex("男");
            user.setBirthday(new Date());
            System.out.println("保存操作之前" + user);
            // 执行保存方法
            userDao.saveUser(user);
    
            // 提交事务
            sqlSession.commit();
    
            System.out.println("保存操作之后" + user);
        }
    
  3. 最后的结果是:

    2019-08-22 18:18:43,515 0      [           main] DEBUG ache.ibatis.logging.LogFactory  - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
    2019-08-22 18:18:43,554 39     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-22 18:18:43,554 39     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-22 18:18:43,554 39     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    2019-08-22 18:18:43,554 39     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
    保存操作之前User{id=null, username='mybatis save user and get id', address='China', sex='男', birthday=Thu Aug 22 18:18:43 CST 2019}
    2019-08-22 18:18:43,668 153    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
    2019-08-22 18:18:44,894 1379   [           main] DEBUG source.pooled.PooledDataSource  - Created connection 2129144075.
    2019-08-22 18:18:44,894 1379   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7ee8290b]
    2019-08-22 18:18:44,899 1384   [           main] DEBUG .itheima.dao.IUserDao.saveUser  - ==>  Preparing: insert into user (username, address, sex, birthday) values (?, ?, ?, ?); 
    2019-08-22 18:18:44,936 1421   [           main] DEBUG .itheima.dao.IUserDao.saveUser  - ==> Parameters: mybatis save user and get id(String), China(String), 男(String), 2019-08-22 18:18:43.646(Timestamp)
    2019-08-22 18:18:44,995 1480   [           main] DEBUG .itheima.dao.IUserDao.saveUser  - <==    Updates: 1
    2019-08-22 18:18:44,996 1481   [           main] DEBUG ao.IUserDao.saveUser!selectKey  - ==>  Preparing: select last_insert_id(); 
    2019-08-22 18:18:44,996 1481   [           main] DEBUG ao.IUserDao.saveUser!selectKey  - ==> Parameters: 
    2019-08-22 18:18:45,015 1500   [           main] DEBUG ao.IUserDao.saveUser!selectKey  - <==      Total: 1
    2019-08-22 18:18:45,017 1502   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7ee8290b]
    保存操作之后User{id=54, username='mybatis save user and get id', address='China', sex='男', birthday=Thu Aug 22 18:18:43 CST 2019}
    2019-08-22 18:18:45,021 1506   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7ee8290b]
    2019-08-22 18:18:45,022 1507   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7ee8290b]
    2019-08-22 18:18:45,022 1507   [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 2129144075 to pool.
    

你可能感兴趣的:(Java基础,Mybatis,框架)