本节包括:
本节包括:
创建一个 maven 项目,不适用任何骨架
向 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>
然后,写实体类和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();
}
写完 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>
之后,因为配置了映射文件的位置,根据 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>
对了,因为我们使用的是 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 框架查询所有数据的过程。
在我们写的查询所有数据的基础上面,我们进行一些添加,就可以完成插入数据的操作。
首先,我们需要进行插入操作,那么我们就应该修改 dao 文件为以下代码,添加了插入的方法:
import com.itheima.domain.User;
import java.util.List;
/**
* 用户持久层
*/
public interface IUserDao {
/**
* 查询所有用户
* @return List
*/
List<User> findAll();
/**
* 保存用户
* @param user
*/
void saveUser(User user);
}
然后,由于在 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 语句。
接着,我们就可以在我们的测试类中使用我们的插入数据的代码了:
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
由于我为了实现写入了两次数据库,所以出现了两次相同的数据
更新的操作具体上和插入数据类似.
首先,我们先在 dao 接口中,添加更新数据的操作。
import com.itheima.domain.User;
import java.util.List;
/**
* 用户持久层
*/
public interface IUserDao {
/**
* 更新用户
* @param user
*/
void updateUser(User user);
}
然后,在映射文件中,添加 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>
最后,回到测试类中,测试我们的更新操作:
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 的数据已经得到了更新
删除操作与更新数据操作、插入数据操作步骤类似
首先在 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语句
<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
进行一个查询的操作过程和以上的过程类似:
在 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);
}
在映射文件中写入以下标签和方法:
<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}
和以上的操作过程相似:
在 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();
}
#{} 表示一个占位符
通过 #{} 可以实现 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 类型之间的转化,调用的预处理方法;而 ${} 使用的是字符串的拼接,谁具有优势不言而喻。
在 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
因为我们设置的数据库的 id 是自动增长的,所以就可以通过以下 sql 语句来获取自动增加的 id
select last_insert_id();
其过程只需要修改之前我们的写保存用户数据的配置信息:
配置一下映射文件,配置文件的内容在之前写的保存用户的配置文件中:
<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语句。
为了更加直观的看到我们已经取回了 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.