Mybatis系列第6篇:恕我直言,mybatis增删改你未必玩得转!

增删改返回值说明

mybatis中对db执行增删改操作,不管是新增、删除、还是修改,最后都会去调用jdbc中对应的方法,要么是调用java.sql.StatementexecuteUpdate的方法,要么是调用java.sql.PreparedStatementexecuteUpdate方法,这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,如下:

<?xml version="1.0" encoding="UTF-8" ?> <![CDATA[ INSERT INTO t_user (id,name,age,salary,sex) VALUES (#{id},#{name},#{age},#{salary},#{sex}) ]]> <![CDATA[ UPDATE t_user SET name = #{name},age = #{age},salary = #{salary},sex = #{sex} WHERE id = #{id} ]]> <![CDATA[ DELETE FROM t_user WHERE id = #{id} ]]>
创建属性配置文件

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,如下:

<?xml version="1.0" encoding="UTF-8" ?>
引入logback日志支持

chat04\src\main\resources目录创建logback.xml,如下:

<?xml version="1.0" encoding="UTF-8"?>

%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开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

学习分享,共勉

这里是小编拿到的学习资源,其中包括“中高级Java开发面试高频考点题笔记300道.pdf”和“Java核心知识体系笔记.pdf”文件分享,内容丰富,囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。同时还有Java进阶学习的知识笔记脑图(内含大量学习笔记)!

资料整理不易,读者朋友可以转发分享下!

Java核心知识体系笔记.pdf

记一次蚂蚁金服Java研发岗的面试经历,分享下我的复习笔记面经

中高级Java开发面试高频考点题笔记300道.pdf

记一次蚂蚁金服Java研发岗的面试经历,分享下我的复习笔记面经

架构进阶面试专题及架构学习笔记脑图

记一次蚂蚁金服Java研发岗的面试经历,分享下我的复习笔记面经

Java架构进阶学习视频分享
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
rty=“id”>

<![CDATA[ INSERT INTO t_user (name,age,salary,sex) VALUES (#{name},#{age},#{salary},#{sex}) ]]>

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-x0w9bSSe-1713500937628)]

[外链图片转存中…(img-bt5eDKSO-1713500937631)]

[外链图片转存中…(img-wPPRMVI9-1713500937632)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

学习分享,共勉

这里是小编拿到的学习资源,其中包括“中高级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架构进阶学习视频分享
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值