增删改返回值说明
mybatis中对db执行增删改操作,不管是新增、删除、还是修改,最后都会去调用jdbc中对应的方法,要么是调用java.sql.Statement
的executeUpdate
的方法,要么是调用java.sql.PreparedStatement
的executeUpdate
方法,这2个类的方法名称都是executeUpdate
,他们的参数可能不一样,但是他们的返回值都是int,说明增删改的返回值都是int类型的,表示影响的行数,比如插入成功1行返回结果就是1,删除了10行记录,返回就是10,更新了5行记录,返回的就是5。
那么我们通过Mybatis中的Mapper接口来对db增删改的时候,mybatis的返回值支持哪些类型呢?
int类型那肯定是支持的,jdbc执行增删改默认返回int类型,那mybatis当然也支持这个类型。
但是mybatis的返回值比jdbc更强大,对于增删改还支持下面几种类型:
int
Integer
long
Long
boolean
Boolean
void
mapper的增删改方法返回值必须为上面的类型,mybatis内部将jdbc返回的int类型转换为上面列表中指定的类型,我们来看一下mybatis这块的源码,源码在下面的方法中:
org.apache.ibatis.binding.MapperMethod#rowCountResult
我们来看一下这个方法的源码:
private Object rowCountResult(int rowCount) {
final Object result;
if (method.returnsVoid()) {
result = null;
} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
result = rowCount;
} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
result = (long)rowCount;
} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
result = rowCount > 0;
} else {
throw new BindingException(“Mapper method '” + command.getName() + "’ has an unsupported return type: " + method.getReturnType());
}
return result;
}
mybatis中会使用上面这个方法最后会对jdbc 增删改返回的int结果进行处理,处理为mapper接口中增删改方法返回值的类型。
int、Integer、long、Long我们就不说了,主要说一下返回值是boolean、Boolean类型,如果影响的行数大于0了,将返回true。
下面我们来创建一个工程感受一下增删改各种返回值。
创建案例
整个mybatis系列的代码采用maven模块的方式管理的,可以在文章底部获取,本次我们还是在上一篇的mybatis-series
中进行开发,在这个项目中新建一个模块chat04
,模块坐标如下:
com.javacode2018
chat04
1.0-SNAPSHOT
下面我们通过mybatis快速来实现对t_user表增删改。
创建UserModel类
mybatis-series\chat04\src\main\java\com\javacode2018\chat04\demo1\model
目录创建UserModel.java
,如下:
package com.javacode2018.chat04.demo1.model;
import lombok.*;
/**
* 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class UserModel {
private Long id;
private String name;
private Integer age;
private Double salary;
private Integer sex;
}
创建UserMapper接口
mybatis-series\chat04\src\main\java\com\javacode2018\chat04\demo1\mapper
目录创建UserMapper.java
,如下:
package com.javacode2018.chat04.demo1.mapper;
import com.javacode2018.chat04.demo1.model.UserModel;
/**
* 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
*/
public interface UserMapper {
/**
* 插入用户信息,返回影响行数
* @param model
* @return
*/
int insertUser(UserModel model);
/**
* 更新用户信息,返回影响行数
* @param model
* @return
*/
long updateUser(UserModel model);
/**
* 根据用户id删除用户信息,返回删除是否成功
* @param userId
* @return
*/
boolean deleteUser(Long userId);
}
注意上面3个操作的返回类型,我们体验一下
int、long、boolean
类型的返回值。
创建UserMapper.xml文件
mybatis-series\chat04\src\main\resources\demo1
目录创建,UserMapper.xml
,如下:
创建属性配置文件
mybatis-series\chat04\src\main\resources
目录中创建jdbc.properties
,如下:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root123
创建mybatis全局配置文件
mybatis-series\chat04\src\main\resources\demo1
目录创建,mybatis-config.xml,如下:
引入logback日志支持
chat04\src\main\resources
目录创建logback.xml
,如下:
%d{mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
创建测试用例Demo1Test
mybatis-series\chat04\src\test\java\com\javacode2018\chat04
目录创建Demo1Test.java
,如下:
package com.javacode2018.chat04;
import com.javacode2018.chat04.demo1.mapper.UserMapper;
import com.javacode2018.chat04.demo1.model.UserModel;
import lombok.extern.slf4j.Slf4j;
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.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
/**
* 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
*/
@Slf4j
public class Demo1Test {
private SqlSessionFactory sqlSessionFactory;
@Before
public void before() throws IOException {
//指定mybatis全局配置文件
String resource = “demo1/mybatis-config.xml”;
//读取全局配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//构建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
this.sqlSessionFactory = sqlSessionFactory;
}
@Test
public void insertUser() {
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//创建UserModel对象
UserModel userModel = UserModel.builder().id(1L).name(“路人甲Java”).age(30).salary(50000D).sex(1).build();
//执行插入操作
int insert = mapper.insertUser(userModel);
log.info(“影响行数:{}”, insert);
}
}
@Test
public void updateUser() {
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//创建UserModel对象
UserModel userModel = UserModel.builder().id(1L).name(“路人甲Java,你好”).age(18).salary(5000D).sex(0).build();
//执行更新操作
long result = mapper.updateUser(userModel);
log.info(“影响行数:{}”, result);
}
}
@Test
public void deleteUser() {
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//定义需要删除的用户id
Long userId = 1L;
//执行删除操作
boolean result = mapper.deleteUser(userId);
log.info(“第1次删除:id={},返回值:{}”, userId, result);
result = mapper.deleteUser(userId);
log.info(“第2次删除:id={},返回值:{}”, userId, result);
}
}
}
项目结构如下图
注意项目结构如下图,跑起来有问题的可以对照一下。
运行测试用例
测试int类型返回值
运行com.javacode2018.chat04.Demo1Test#insertUser
,插入一条用户信息,输出如下:
16:35.821 [main] DEBUG c.j.c.d.mapper.UserMapper.insertUser - ==> Preparing: INSERT INTO t_user (id,name,age,salary,sex) VALUES (?,?,?,?,?)
16:35.858 [main] DEBUG c.j.c.d.mapper.UserMapper.insertUser - ==> Parameters: 1(Long), 路人甲Java(String), 30(Integer), 50000.0(Double), 1(Integer)
16:35.865 [main] DEBUG c.j.c.d.mapper.UserMapper.insertUser - <== Updates: 1
16:35.865 [main] INFO com.javacode2018.chat04.Demo1Test - 影响行数:1
测试long类型返回值
运行com.javacode2018.chat04.Demo1Test#updateUser
,通过用户id更新用户信息,输出如下:
17:49.084 [main] DEBUG c.j.c.d.mapper.UserMapper.updateUser - ==> Preparing: UPDATE t_user SET name = ?,age = ?,salary = ?,sex = ? WHERE id = ?
17:49.127 [main] DEBUG c.j.c.d.mapper.UserMapper.updateUser - ==> Parameters: 路人甲Java,你好(String), 18(Integer), 5000.0(Double), 0(Integer), 1(Long)
17:49.135 [main] DEBUG c.j.c.d.mapper.UserMapper.updateUser - <== Updates: 1
17:49.135 [main] INFO com.javacode2018.chat04.Demo1Test - 影响行数:1
测试boolean类型返回值
运行com.javacode2018.chat04.Demo1Test#deleteUser
,根据用户id删除用户信息,删除2次,输出如下:
20:37.745 [main] DEBUG c.j.c.d.mapper.UserMapper.deleteUser - ==> Preparing: DELETE FROM t_user WHERE id = ?
20:37.785 [main] DEBUG c.j.c.d.mapper.UserMapper.deleteUser - ==> Parameters: 1(Long)
20:37.790 [main] DEBUG c.j.c.d.mapper.UserMapper.deleteUser - <== Updates: 0
20:37.791 [main] INFO com.javacode2018.chat04.Demo1Test - 第1次删除:id=1,返回值:false
20:37.793 [main] DEBUG c.j.c.d.mapper.UserMapper.deleteUser - ==> Preparing: DELETE FROM t_user WHERE id = ?
20:37.794 [main] DEBUG c.j.c.d.mapper.UserMapper.deleteUser - ==> Parameters: 1(Long)
20:37.795 [main] DEBUG c.j.c.d.mapper.UserMapper.deleteUser - <== Updates: 0
20:37.795 [main] INFO com.javacode2018.chat04.Demo1Test - 第2次删除:id=1,返回值:false
第一次删除成功,再次删除数据已经不存在了,返回
false
。
jdbc获取主键的几种方式
上面的案例中inserUser
会向t_user
表插入数据,t_user
表的id
是自动增长的,插入数据的时候我们不指定id的值,看看插入成功之后userModel
对象和db中插入的记录是什么样的。
com.javacode2018.chat04.Demo1Test#insertUser
代码改成下面这样:
@Test
public void insertUser() {
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//创建UserModel对象
UserModel userModel = UserModel.builder().name(“郭富城”).age(30).salary(50000D).sex(1).build();
//执行插入操作
int insert = mapper.insertUser(userModel);
log.info(“影响行数:{}”, insert);
log.info(“{}”, userModel);
}
}
执行一下,输出:
36:10.673 [main] DEBUG c.j.c.d.mapper.UserMapper.insertUser - ==> Preparing: INSERT INTO t_user (id,name,age,salary,sex) VALUES (?,?,?,?,?)
36:10.715 [main] DEBUG c.j.c.d.mapper.UserMapper.insertUser - ==> Parameters: null, 郭富城(String), 30(Integer), 50000.0(Double), 1(Integer)
36:10.721 [main] DEBUG c.j.c.d.mapper.UserMapper.insertUser - <== Updates: 1
36:10.722 [main] INFO com.javacode2018.chat04.Demo1Test - 影响行数:1
36:10.723 [main] INFO com.javacode2018.chat04.Demo1Test - UserModel(id=null, name=郭富城, age=30, salary=50000.0, sex=1)
输出中插入成功1行,最后一行日志中输出了userModel
对象所有属性信息,id是null
的,我们去db中看一下这条记录:
mysql> SELECT * FROM t_user;
±—±----------±----±---------±----+
| id | name | age | salary | sex |
±—±----------±----±---------±----+
| 2 | 郭富城 | 30 | 50000.00 | 1 |
±—±----------±----±---------±----+
1 row in set (0.00 sec)
db中插入的这条郭富城
的id是2,当我们没有指定id,或者指定的id为null的时候,mysql会自动生成id的值。
那么我们如何mysql中获取这个自动增长的值呢?我们先看看jdbc是如何实现的
方式1:jdbc内置的方式
用法
jdbc的api中为我们提供了获取自动生成主键的值,具体看这个方法:
java.sql.Statement#getGeneratedKeys
看一下这个方法的定义:
/**
* Retrieves any auto-generated keys created as a result of executing this
* Statement
object. If this Statement
object did
* not generate any keys, an empty ResultSet
* object is returned.
*
Note:If the columns which represent the auto-generated keys were not specified,
* the JDBC driver implementation will determine the columns which best represent the auto-generated keys.
* @return a ResultSet
object containing the auto-generated key(s)
* generated by the execution of this Statement
object
* @exception SQLException if a database access error occurs or
* this method is called on a closed Statement
* @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method
* @since 1.4
*/
ResultSet getGeneratedKeys() throws SQLException;
这个方法会返回一个结果集,从这个结果集中可以获取自增主键的值。
不过使用这个方法有个前提,执行sql的时候需要做一个设置。
如果是通过java.sql.Statement
执行sql,需要调用下面这个方法:
int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException
注意上面这个方法的第二个参数需要设置为java.sql.Statement.RETURN_GENERATED_KEYS
,表示需要返回自增列的值。
不过多数情况下,我们会使用java.sql.PreparedStatement
对象来执行sql,如果想获取自增值,创建这个对象需要设置第2个参数的值,如下:
PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
然后我们就可以通过getGeneratedKeys
返回的ResultSet
对象获取自动增长的值了,如下:
ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
if (generatedKeys!=null && generatedKeys.next()) {
log.info(“自增值为:{}”, generatedKeys.getInt(1));
}
案例
com.javacode2018.chat04.Demo1Test
中新增一个测试用例,如下代码:
private String jdbcDriver = “com.mysql.jdbc.Driver”;
private String jdbcUrl = “jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8”;
private String jdbcUserName = “root”;
private String jdbcPassword = “root123”;
@Test
public void jdbcInsertUser1() throws Exception {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet generatedKeys = null;
try {
UserModel userModel = UserModel.builder().name(“黎明”).age(30).salary(50000D).sex(1).build();
//执行jdbc插入数据操作
Class.forName(jdbcDriver);
connection = DriverManager.getConnection(jdbcUrl, jdbcUserName, jdbcPassword);
//注意创建PreparedStatement的时候,使用prepareStatement方法的第二个参数需要指定Statement.RETURN_GENERATED_KEYS
preparedStatement = connection.prepareStatement(“INSERT INTO t_user (name,age,salary,sex) VALUES (?,?,?,?)”, Statement.RETURN_GENERATED_KEYS);
int parameterIndex = 1;
preparedStatement.setString(parameterIndex++, userModel.getName());
preparedStatement.setInt(parameterIndex++, userModel.getAge());
preparedStatement.setDouble(parameterIndex++, userModel.getSalary());
preparedStatement.setInt(parameterIndex++, userModel.getSex());
int count = preparedStatement.executeUpdate();
log.info(“影响行数:{}”, count);
//获取自增值
generatedKeys = preparedStatement.getGeneratedKeys();
if (generatedKeys != null && generatedKeys.next()) {
log.info(“自增值为:{}”, generatedKeys.getInt(1));
}
} finally {
if (generatedKeys != null && generatedKeys.isClosed()) {
generatedKeys.close();
}
if (preparedStatement != null && preparedStatement.isClosed()) {
preparedStatement.close();
}
if (connection != null && connection.isClosed()) {
connection.close();
}
}
}
上面代码中我们插入了一条用户的信息,没有指定用户的id,执行输出:
21:22.410 [main] INFO com.javacode2018.chat04.Demo1Test - 影响行数:1
21:22.414 [main] INFO com.javacode2018.chat04.Demo1Test - 自增值为:5
我们去db中看一下这个记录的id,如下,确实是5:
mysql> SELECT * FROM t_user;
±—±-------±----±---------±----+
| id | name | age | salary | sex |
±—±-------±----±---------±----+
| 5 | 黎明 | 30 | 50000.00 | 1 |
±—±-------±----±---------±----+
1 row in set (0.00 sec)
方式2:插入之后查询获取
用法
mysql中插入一条数据之后,可以通过下面的sql获取最新插入记录的id的值:
SELECT LAST_INSERT_ID()
那么我们可以在插入之后,立即使用当前连接发送上面这条sql去获取自增列的值就可以。
案例
创建测试用例com.javacode2018.chat04.Demo1Test#jdbcInsertUser2
,代码如下:
@Test
public void jdbcInsertUser2() throws Exception {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet rs = null;
try {
UserModel userModel = UserModel.builder().name(“梁朝伟”).age(30).salary(50000D).sex(1).build();
//执行jdbc插入数据操作
Class.forName(jdbcDriver);
connection = DriverManager.getConnection(jdbcUrl, jdbcUserName, jdbcPassword);
//注意创建PreparedStatement的时候,使用prepareStatement方法的第二个参数需要指定Statement.RETURN_GENERATED_KEYS
preparedStatement = connection.prepareStatement(“INSERT INTO t_user (name,age,salary,sex) VALUES (?,?,?,?)”, Statement.RETURN_GENERATED_KEYS);
int parameterIndex = 1;
preparedStatement.setString(parameterIndex++, userModel.getName());
preparedStatement.setInt(parameterIndex++, userModel.getAge());
preparedStatement.setDouble(parameterIndex++, userModel.getSalary());
preparedStatement.setInt(parameterIndex++, userModel.getSex());
int count = preparedStatement.executeUpdate();
log.info(“影响行数:{}”, count);
//通过查询获取自增值
rs = connection.prepareStatement(“SELECT LAST_INSERT_ID()”).executeQuery();
if (rs != null && rs.next()) {
log.info(“自增值为:{}”, rs.getInt(1));
}
} finally {
if (rs != null && rs.isClosed()) {
rs.close();
}
if (preparedStatement != null && preparedStatement.isClosed()) {
preparedStatement.close();
}
if (connection != null && connection.isClosed()) {
connection.close();
}
}
}
运行输出:
26:55.407 [main] INFO com.javacode2018.chat04.Demo1Test - 影响行数:1
26:55.414 [main] INFO com.javacode2018.chat04.Demo1Test - 自增值为:6
db中我们去看一下,梁朝伟
的id是6,如下:
mysql> SELECT * FROM t_user;
±—±----------±----±---------±----+
| id | name | age | salary | sex |
±—±----------±----±---------±----+
| 5 | 黎明 | 30 | 50000.00 | 1 |
| 6 | 梁朝伟 | 30 | 50000.00 | 1 |
±—±----------±----±---------±----+
2 rows in set (0.00 sec)
方式3:插入之前获取
oracle不知道大家有没有玩过,oracle中没有mysql中自动增长列,但是oracle有个功能可以实现自动增长,这个功能就是序列
,序列就相当于一个自增器一样,有个初始值,每次递增的步长,当然这个序列提供了一些功能给我们使用,可以获取序列的当前值、下一个值,使用方式如下:
1.先定义一个序列
2.获取下一个值:SELECT 序列名.NEXTVAL FROM dual;
这个案例我只说一下具体步骤,代码就不写了,步骤:
1.通过jdbc执行SELECT 序列名.NEXTVAL FROM dual
获取序列的下一个值,如nextId
2.在代码中使用nextId的值
上面就是jdbc获取值增值的几种方式,jdbc中的这3中方式,mybatis中都提供了对应的 支持,下面我们来看mybatis中是如何实现的。
mybatis获取主键的3种方式
方式1:内部使用jdbc内置的方式
用法
mybatis这个方式内部采用的是上面说的jdbc内置的方式。
我们需要在Mapper xml中进行配置,如:
<![CDATA[ INSERT INTO t_user (name,age,salary,sex) VALUES (#{name},#{age},#{salary},#{sex}) ]]>自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
学习分享,共勉
这里是小编拿到的学习资源,其中包括“中高级Java开发面试高频考点题笔记300道.pdf”和“Java核心知识体系笔记.pdf”文件分享,内容丰富,囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。同时还有Java进阶学习的知识笔记脑图(内含大量学习笔记)!
资料整理不易,读者朋友可以转发分享下!
Java核心知识体系笔记.pdf
中高级Java开发面试高频考点题笔记300道.pdf
架构进阶面试专题及架构学习笔记脑图
Java架构进阶学习视频分享
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
rty=“id”>
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-x0w9bSSe-1713500937628)]
[外链图片转存中…(img-bt5eDKSO-1713500937631)]
[外链图片转存中…(img-wPPRMVI9-1713500937632)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
学习分享,共勉
这里是小编拿到的学习资源,其中包括“中高级Java开发面试高频考点题笔记300道.pdf”和“Java核心知识体系笔记.pdf”文件分享,内容丰富,囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。同时还有Java进阶学习的知识笔记脑图(内含大量学习笔记)!
资料整理不易,读者朋友可以转发分享下!
Java核心知识体系笔记.pdf
[外链图片转存中…(img-xcqbvAL0-1713500937633)]
中高级Java开发面试高频考点题笔记300道.pdf
[外链图片转存中…(img-y1V4Ca0V-1713500937635)]
架构进阶面试专题及架构学习笔记脑图
[外链图片转存中…(img-oeXfyS4E-1713500937636)]
Java架构进阶学习视频分享
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!