文章目录
使用 Mybatis 完成 CRUD
本节包括:
- curd
- 参数深入及结果集的深入
- 基于传统的 dao 的方式 (编写 dao 实现类) – 了解内容
使用 Mybatis 完成 CRUD
本节包括:
- curd
- 参数深入及结果集的深入
- 基于传统的 dao 的方式 (编写 dao 实现类) – 了解内容
- 主配置文件 - properties 标签 - typeAliases 标签 - mappers 标签
1. 使用 Mybatis 进行查询所有的操作
-
创建一个 maven 项目,不适用任何骨架
-
向 maven 项目的 pom.xml 中添加一下依赖项:
<?xml version="1.0" encoding="UTF-8"?> <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.0</modelVersion> <groupId>com.itheima</groupId> <artifactId>maven_mybatis_curd</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.17</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> </project>
-
然后,写实体类和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<User> */ List<User> findAll(); }
-
写完 dao 接口类 和 实体类之后,在 resources 文件下面配置 Mybatis 的主配置文件(这个名字是任意的),里面存放了连接数据库的信息,和 mysql语句 映射文件的位置
<?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> <!-- 配置环境--> <environments default="mysql"> <!-- 配置 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>
-
之后,因为配置了映射文件的位置,根据 Mybatis 的要求,我们需要在 resources 资源文件下创建一个和 dao 接口文件目录结构相同的 xml 文件,这个 xml 文件用来存放我们需要使用的 sql 语句(这个 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.itheima.dao.IUserDao"> <!-- 映射的方法--> <!-- resultType 封装的查询结果集的对象--> <!-- 查询所有操作--> <select id="findAll" resultType="com.itheima.domain.User"> select * from user; </select> </mapper>
-
对了,因为我们使用的是 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
-
然后,就可以通过测试类来查询 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(); } }
-
最后,是运行的结果:
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 进行插入数据的操作
在我们写的查询所有数据的基础上面,我们进行一些添加,就可以完成插入数据的操作。
-
首先,我们需要进行插入操作,那么我们就应该修改 dao 文件为以下代码,添加了插入的方法:
import com.itheima.domain.User; import java.util.List; /** * 用户持久层 */ public interface IUserDao { /** * 查询所有用户 * @return List<User> */ List<User> findAll(); /** * 保存用户 * @param user */ void saveUser(User user); }
-
然后,由于在 dao 类中添加了查询的方法,如果想要使用,我们必须在映射文件添加 SQL 语句,将 resources 文件夹下面,与 dao 接口相同文件结构的 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.itheima.dao.IUserDao"> <!-- 映射的方法--> <!-- resultType 封装的查询结果集的对象--> <!-- 查询所有操作--> <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方法后面的名称。
接下来,按照之前的分析,补全映射文件:
<?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.itheima.dao.IUserDao"> <!-- 映射的方法--> <!-- resultType 封装的查询结果集的对象--> <!-- 查询所有操作--> <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 语句。
-
接着,我们就可以在我们的测试类中使用我们的插入数据的代码了:
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();
我们手动将事务提交,那么就可以实现插入数据了。
-
以下是运行成功时的结果:
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 进行更新的操作
更新的操作具体上和插入数据类似.
-
首先,我们先在 dao 接口中,添加更新数据的操作。
import com.itheima.domain.User; import java.util.List; /** * 用户持久层 */ public interface IUserDao { /** * 更新用户 * @param user */ void updateUser(User user); }
-
然后,在映射文件中,添加 SQL 语句
<?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.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>
-
最后,回到测试类中,测试我们的更新操作:
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。
-
接下来是运行结果:
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.
-
以下为数据库中的结果:
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 进行删除的操作
删除操作与更新数据操作、插入数据操作步骤类似
-
首先在 dao 接口中,写上删除的方法:
package com.itheima.dao; import com.itheima.domain.User; import java.util.List; /** * 用户持久层 */ public interface IUserDao { /** * 根据 id 删除用户 * @param id */ void deleteUser(Integer id); }
-
然后,在映射文件中,写上需要执行的SQL语句
<?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.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 仅仅只是一个占位符,写什么都可以。
-
最后,在测试类中测试删除方法:
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(); } }
-
执行测试类,发现控制台没有报错。然后查看一下数据库,发现 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 进行查询一个操作
进行一个查询的操作过程和以上的过程类似:
-
在 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); }
-
在映射文件中写入以下标签和方法:
<?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.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 对象中。
-
然后在测试类中写入以下测试方法:
@Test public void testFindOne() { // 执行查询一个方法 User user = userDao.findById(51); System.out.println(user); sqlSession.commit(); }
-
运行,成功查询出结果:
User{id=51, username='mybatis update user', address='China', sex='女', birthday=Wed Aug 21 23:19:40 CST 2019}
6. 使用 Mybatis 框架进行模糊查询操作
6.1 查询的过程
和以上的操作过程相似:
-
在 Dao 接口中,写入模糊查询的方法:
/** * 根据名称,模糊查询用户信息 * @param username * @return */ List<User> findByName(String username);
-
在映射文件中写入以下标签:
<!-- 根据名称模糊查询的语法--> <select id="findByName" parameterType="String" resultType="com.itheima.domain.User"> select * from user where username like #{name}; </select>
这里注意,我们传入的参数类型是一个 String 类型,同时我们必须设置结果集类型,以便 Mybatis 框架实现结果集的封装。
-
在测试类中使用测试方法来测试正确与否:
@Test public void testFindByName() { // 执行查询一个方法 List<User> users = userDao.findByName("%王"); for(User user : users) { System.out.println(user); } sqlSession.commit(); }
由于模糊查询需要使用 % 来进行查询所有包括 王 字的人。又因为我们在配置文件并没有添加 %,所以我们在传入参数时,必须手动加入。
-
运行之后,发现没有问题,同时查询出的结果如下:
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 使用聚合查询查询结果
-
在 dao 接口中写入查询数据库中数据总条数的方法:
/** * 查询总用户数 * @return */ int findTotal();
-
然后在映射文件写入以下配置:
<!-- 获取用户的总记录条数--> <select id="findTotal" resultType="int"> select count(id) from user; </select>
-
然后,在测试类中写入以下测试方法,并执行:
/** * 测试查询总记录条数 */ @Test public void testFindTotal() { // 执行查询一个方法 int total = userDao.findTotal(); sqlSession.commit(); System.out.println(total); }
-
以下是结果:
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();
其过程只需要修改之前我们的写保存用户数据的配置信息:
-
配置一下映射文件,配置文件的内容在之前写的保存用户的配置文件中:
<insert id="saveUser" parameterType="com.itheima.domain.User"> <!-- 配置插入操作后,获取去插入数据的id --> <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语句。
-
为了更加直观的看到我们已经取回了 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); }
-
最后的结果是:
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.