Mybatis系列第8篇:自动映射,使用需谨慎!

Mybatis系列目标:从入门开始开始掌握一个高级开发所需要的Mybatis技能。

这是mybatis系列第8篇。

案例代码

  1. 链接:https://pan.baidu.com/s/1vt-MAX3oJOu9gyxZAhKkbg 
  2. 提取码:i8op

本文案例代码入口,配合源码看案例,效果更好。

mybatis-series\chat05\src\test\java\com\javacode2018\chat05\demo7\Demo7Test.java

什么是自动映射?

介绍自动映射之前先看一下手动映射,如下:

  1. <resultMap id="orderModelMap1" type="com.javacode2018.chat05.demo7.model.OrderModel">
  2.     <id column="id" property="id"/>
  3.     <result column="userId" property="userId" />
  4.     <result column="createTime" property="createTime" />
  5.     <result column="upTime" property="upTime" />
  6. </resultMap>
  7. <select id="getById1" resultMap="orderModelMap1">
  8.     <![CDATA[
  9.     SELECT
  10.         a.id,
  11.         a.user_id userId,
  12.         a.create_time createTime,
  13.         a.up_time upTime
  14.     FROM
  15.         t_order a
  16.     WHERE
  17.         a.id = #{value}
  18.     ]]>
  19. </select>

注意上面的resultMap元素中有4行配置,如下:

  1. <id column="id" property="id"/>
  2. <result column="userId" property="userId" />
  3. <result column="createTime" property="createTime" />
  4. <result column="upTime" property="upTime" />

这4行代码用于配置sql结果的列和OrderModel对象中字段的映射关系。

大家有没有注意到,映射规则中column和property元素的值都是一样,mybatis中支持自动映射配置,当开启自动映射之后,当sql的列名和Model中的字段名称是一样的时候(不区分大小写),mybatis内部会进行自动映射,不需要我们手动去写上面的4行映射规则。

下面我们将上面的示例改成自动映射的方式,如下:

  1. <resultMap id="orderModelMap2" type="com.javacode2018.chat05.demo7.model.OrderModel" autoMapping="true">
  2. </resultMap>
  3. <select id="getById2" resultMap="orderModelMap2">
  4.     <![CDATA[
  5.     SELECT
  6.         a.id,
  7.         a.user_id userId,
  8.         a.create_time createTime,
  9.         a.up_time upTime
  10.     FROM
  11.         t_order a
  12.     WHERE
  13.         a.id = #{value}
  14.     ]]>
  15. </select>

注意上面的resultMap中的autoMapping属性,是否开启自动映射,我们设置为true,这样mybatis会自动按照列名和Model中同名的字段进行映射赋值。

上面两个配置最后查询结果是一样的,都会将查询结果对应的4个字段的值自动赋值给OrderModel中同名的属性。

自动映射开关

mybatis中自动映射主要有2种配置,一种是全局的配置,对应用中所有的resultMap起效,这个是在mybatis配置文件中进行设置的;另外一种是通过resultMapautoMapping属性进行配置。

mybatis判断某个resultMap是否开启自动映射配置的时候,会先查找自身的autoMapping属性,如果这个属性设置值了,就直接用这个属性的值,如果resultMap元素的autoMapping属性没有配置,则走全局配置的自动映射规则。

下面我们来详解介绍一下这款的内容。

mybatis自动映射全局配置

在mybatis全局配置文件中加入下面配置:

  1. <settings>
  2.     <setting name="autoMappingBehavior" value="自动映射规则"/>
  3. </settings>

autoMappingBehavior值来源于枚举:org.apache.ibatis.session.AutoMappingBehavior,源码:

  1. public enum AutoMappingBehavior {
  2.   /**
  3.    * Disables auto-mapping.
  4.    */
  5.   NONE,
  6.   /**
  7.    * Will only auto-map results with no nested result mappings defined inside.
  8.    */
  9.   PARTIAL,
  10.   /**
  11.    * Will auto-map result mappings of any complexity (containing nested or otherwise).
  12.    */
  13.   FULL
  14. }
  • NONE:关闭全局映射开关

  • PARTIAL:对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射,这个也是默认值。

  • FULL:自动映射所有属性。

小提示:settings元素中有很多配置,这些配置最后都会被解析成org.apache.ibatis.session.Configuration的属性,源码位于org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsElement方法中。

下面我们来演示一下autoMappingBehavior每种配置的效果。

NONE

mybatis-config.xml加入配置
  1. <settings>
  2.     <!-- 关闭自动映射开关 -->
  3.     <setting name="autoMappingBehavior" value="NONE"/>
  4. </settings>
OrderMapper.xml
  1. <resultMap id="orderModelMap4" type="com.javacode2018.chat05.demo7.model.OrderModel">
  2. </resultMap>
  3. <select id="getById4" resultMap="orderModelMap4">
  4.     <![CDATA[
  5.     SELECT
  6.         a.id,
  7.         a.user_id userId,
  8.         a.create_time createTime,
  9.         a.up_time upTime
  10.     FROM
  11.         t_order a
  12.     WHERE
  13.         a.id = #{value}
  14.     ]]>
  15. </select>
OrderMapper.java加入
OrderModel getById4(int id);
测试用例
  1. com.javacode2018.chat05.demo7.Demo7Test#getById4
  2. @Test
  3. public void getById4() throws IOException {
  4.     this.before("demo7/mybatis-config1.xml");
  5.     try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
  6.         OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
  7.         OrderModel orderModel = mapper.getById4(2);
  8.         log.info("{}", orderModel);
  9.     }
  10. }
运行结果
  1. 21:58.821 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById4 - ==>  Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime FROM t_order a WHERE a.id = ? 
  2. 21:58.850 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById4 - ==> Parameters: 2(Integer)
  3. 21:58.868 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById4 - <==      Total: 1
  4. 21:58.868 [main] INFO  c.j.chat05.demo7.Demo7Test - null

从输出中可以看到最后一样输出结果为null,sql实际上返回的是有结果的,但是结果映射的时候返回的是空。

结果解释

由于mybatis全局配置中将autoMappingBehavior的值置为了NONE,表示全局自动映射被关闭了,而resultMapper中的orderModelMap4没有配置autoMapping属性,所以最终这个查询结果不会自动映射,所以最后查询结果为null。

PARTIAL

对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射,这个也是autoMappingBehavior的默认值。

mybatis-config.xml加入配置
  1. <settings>
  2.     <!-- 对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射,这个也是autoMappingBehavior的默认值。 -->
  3.     <setting name="autoMappingBehavior" value="PARTIAL"/>
  4. </settings>
OrderMapper.xml
  1. <resultMap id="orderModelMap5" type="com.javacode2018.chat05.demo7.model.OrderModel">
  2. </resultMap>
  3. <select id="getById5" resultMap="orderModelMap5">
  4.     <![CDATA[
  5.     SELECT
  6.         a.id,
  7.         a.user_id userId,
  8.         a.create_time createTime,
  9.         a.up_time upTime
  10.     FROM
  11.         t_order a
  12.     WHERE
  13.         a.id = #{value}
  14.     ]]>
  15. </select>
OrderMapper.java加入
OrderModel getById5(int id);
测试用例
  1. com.javacode2018.chat05.demo7.Demo7Test#getById5
  2. @Test
  3. public void getById5() throws IOException {
  4.     this.before("demo7/mybatis-config2.xml");
  5.     try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
  6.         OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
  7.         OrderModel orderModel = mapper.getById5(2);
  8.         log.info("{}", orderModel);
  9.     }
  10. }
运行结果
  1. 28:32.612 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById5 - ==>  Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime FROM t_order a WHERE a.id = ? 
  2. 28:32.648 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById5 - ==> Parameters: 2(Integer)
  3. 28:32.664 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById5 - <==      Total: 1
  4. 28:32.665 [main] INFO  c.j.chat05.demo7.Demo7Test - OrderModel(id=2, userId=1, createTime=1577947790, upTime=1577947790, userModel=null)

OrderModel中的4个属性被自动映射成功了。

结果解释

orderModelMap5中没有指定autoMapping属性,所以自动映射会走全局配置的规则,即PARTIAL,会进行自动映射。

我们再来看看PARTIAL的解释:对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射。这句话是什么意思?

有些复杂的查询映射会在resultMap中嵌套一些映射(如:association,collection),当使用PARTIAL的时候,如果有嵌套映射,则这个嵌套映射不会进行自动映射了。

通过订单id查询出订单以及订单用户的信息,sqlmap如下:

  1. <resultMap id="orderModelMap6" type="com.javacode2018.chat05.demo7.model.OrderModel">
  2.     <association property="userModel">
  3.     </association>
  4. </resultMap>
  5. <select id="getById6" resultMap="orderModelMap6">
  6.     <![CDATA[
  7.     SELECT
  8.         a.id,
  9.         a.user_id userId,
  10.         a.create_time createTime,
  11.         a.up_time upTime
  12.         b.id as user_id,
  13.         b.name
  14.     FROM
  15.         t_order a,t_user b
  16.     WHERE
  17.         a.user_id = b.id
  18.         AND a.id = #{value}
  19.     ]]>
  20. </select>
OrderModel.java
  1. package com.javacode2018.chat05.demo7.model;
  2. import lombok.*;
  3. import java.util.List;
  4. @Getter
  5. @Setter
  6. @Builder
  7. @ToString
  8. @NoArgsConstructor
  9. @AllArgsConstructor
  10. public class OrderModel {
  11.     private Integer id;
  12.     private Integer userId;
  13.     private Long createTime;
  14.     private Long upTime;
  15.     private UserModel userModel;
  16. }

内部有个userModel属性引用用户对象。

UserModel.java
  1. package com.javacode2018.chat05.demo7.model;
  2. import lombok.*;
  3. @Getter
  4. @Setter
  5. @Builder
  6. @ToString
  7. @NoArgsConstructor
  8. @AllArgsConstructor
  9. public class UserModel {
  10.     private Integer id;
  11.     private String name;
  12. }
OrderMapper.java中加入
OrderModel getById6(int id);

测试用例

  1. com.javacode2018.chat05.demo7.Demo7Test#getById6
  2. @Test
  3. public void getById6() throws IOException {
  4.     this.before("demo7/mybatis-config2.xml");
  5.     try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
  6.         OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
  7.         OrderModel orderModel = mapper.getById6(2);
  8.         log.info("{}", orderModel);
  9.     }
  10. }
运行输出
  1. 52:49.037 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - ==>  Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime, b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = ? 
  2. 52:49.066 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - ==> Parameters: 2(Integer)
  3. 52:49.087 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - <==      Total: 1
  4. 52:49.088 [main] INFO  c.j.chat05.demo7.Demo7Test - null

sql查询实际上是有一条记录的,但是最后返回的是null,说明没有进行自动映射。

FULL

自动映射所有属性。

这次Mapper我们不动,还是下面这样,没有手动指定映射规则。

  1. <resultMap id="orderModelMap6" type="com.javacode2018.chat05.demo7.model.OrderModel">
  2.     <association property="userModel">
  3.     </association>
  4. </resultMap>
  5. <select id="getById6" resultMap="orderModelMap6">
  6.     <![CDATA[
  7.     SELECT
  8.         a.id,
  9.         a.user_id userId,
  10.         a.create_time createTime,
  11.         a.up_time upTime
  12.         b.id as user_id,
  13.         b.name
  14.     FROM
  15.         t_order a,t_user b
  16.     WHERE
  17.         a.user_id = b.id
  18.         AND a.id = #{value}
  19.     ]]>
  20. </select>

修改一下autoMappingBehavior的值为FULL,看看效果。

mybatis配置
  1. <settings>
  2.     <!-- 自动映射所有属性 -->
  3.     <setting name="autoMappingBehavior" value="FULL"/>
  4. </settings>
测试用例
  1. com.javacode2018.chat05.demo7.Demo7Test#getById6_0
  2. @Test
  3. public void getById6_0() throws IOException {
  4.     this.before("demo7/mybatis-config3.xml");
  5.     try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
  6.         OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
  7.         OrderModel orderModel = mapper.getById6(2);
  8.         log.info("{}", orderModel);
  9.     }
  10. }
运行输出
  1. 56:05.127 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - ==>  Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime, b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = ? 
  2. 56:05.155 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - ==> Parameters: 2(Integer)
  3. 56:05.186 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - <==      Total: 1
  4. 56:05.186 [main] INFO  c.j.chat05.demo7.Demo7Test - OrderModel(id=2, userId=1, createTime=1577947790, upTime=1577947790, userModel=UserModel(id=2, name=张学友))

输出中可以看到OrderModel所有属性都是有值的,userModel的2个属性也有值,userModel.id是2,我们运行一下sql看看,用户id是多少,如下:

  1. mysql> SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime, b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = 2;
  2. +----+--------+------------+------------+---------+-----------+
  3. | id | userId | createTime | upTime     | user_id | name      |
  4. +----+--------+------------+------------+---------+-----------+
  5. |  2 |      1 | 1577947790 | 1577947790 |       1 | 张学友    |
  6. +----+--------+------------+------------+---------+-----------+
  7. 1 row in set (0.00 sec)

user_id实际上是1,mybatis中按照sql字段和model结果字段同名进行自动映射,所以将订单的id赋值给userModel的id属性了。

此时需要我们orderModelMap6的配置,手动指定一下user_id和userModel.id的映射规则,如下:

  1. <resultMap id="orderModelMap6" type="com.javacode2018.chat05.demo7.model.OrderModel">
  2.     <association property="userModel">
  3.         <id column="user_id" property="id"/>
  4.     </association>
  5. </resultMap>
再次运行测试用例
com.javacode2018.chat05.demo7.Demo7Test#getById6_0
输出
  1. 15:02.751 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - ==>  Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime, b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = ? 
  2. 15:02.783 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - ==> Parameters: 2(Integer)
  3. 15:02.801 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - <==      Total: 1
  4. 15:02.801 [main] INFO  c.j.chat05.demo7.Demo7Test - OrderModel(id=2, userId=1, createTime=1577947790, upTime=1577947790, userModel=UserModel(id=1, name=张学友))

这次userModel中的id正确了。

autoMapping使用

上面我们有说过,当在resultMap中指定了autoMapping属性之后,这个resultMap的自动映射就受autoMapping属性的控制,和mybatis中全局映射配置(autoMappingBehavior)行为无关了。

案例1

这个核心配置主要在sqlmap中,如下:
  1. <resultMap id="orderModelMap7" type="com.javacode2018.chat05.demo7.model.OrderModel" autoMapping="true">
  2.     <association property="userModel" autoMapping="true">
  3.         <id column="user_id" property="id"/>
  4.     </association>
  5. </resultMap>
  6. <select id="getById7" resultMap="orderModelMap7">
  7.     <![CDATA[
  8.     SELECT
  9.         a.id,
  10.         a.user_id userId,
  11.         a.create_time createTime,
  12.         a.up_time upTime,
  13.         b.id as user_id,
  14.         b.name
  15.     FROM
  16.         t_order a,t_user b
  17.     WHERE
  18.         a.user_id = b.id
  19.         AND a.id = #{value}
  20.     ]]>
  21. </select>
对应测试用例
  1. com.javacode2018.chat05.demo7.Demo7Test#getById7
  2. @Test
  3. public void getById7() throws IOException {
  4.     this.before("demo7/mybatis-config1.xml");
  5.     try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
  6.         OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
  7.         OrderModel orderModel = mapper.getById7(2);
  8.         log.info("{}", orderModel);
  9.     }
  10. }
运行输出
  1. 24:37.544 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById7 - ==>  Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime, b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = ? 
  2. 24:37.589 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById7 - ==> Parameters: 2(Integer)
  3. 24:37.610 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById7 - <==      Total: 1
  4. 24:37.610 [main] INFO  c.j.chat05.demo7.Demo7Test - OrderModel(id=2, userId=1, createTime=1577947790, upTime=1577947790, userModel=UserModel(id=1, name=张学友))

OrderModel中所有属性都自动映射成功。

自动装配并不是那么好玩,玩不转可能带来一些隐患,我们看一个案例,见下面的示例2。

示例2

根据订单编号,查询出订单信息,顺便查询出订单明细列表。这个我们使用mybatis中的一对多查询。

OrderDetaiMapper.xml加入
  1. <select id="getListByOrderId1" resultType="com.javacode2018.chat05.demo7.model.OrderDetailModel">
  2.     <![CDATA[
  3.     SELECT
  4.         a.id,
  5.         a.order_id AS orderId,
  6.         a.goods_id AS goodsId,
  7.         a.num,
  8.         a.total_price AS totalPrice
  9.     FROM
  10.         t_order_detail a
  11.     WHERE
  12.         a.order_id = #{value}
  13.     ]]>
  14. </select>

这个可以根据订单的id,查询出订单关联的明细列表。

OrderMapper.xml加入
  1. <resultMap id="orderModelMap8" type="com.javacode2018.chat05.demo7.model.OrderModel" autoMapping="true">
  2.     <collection property="orderDetailModelList" select="com.javacode2018.chat05.demo7.mapper.OrderDetailMapper.getListByOrderId1" column="id"/>
  3. </resultMap>
  4. <select id="getById8" resultMap="orderModelMap8">
  5.     <![CDATA[
  6.     SELECT
  7.         a.id,
  8.         a.user_id userId,
  9.         a.create_time createTime,
  10.         a.up_time upTime
  11.     FROM
  12.         t_order a
  13.     WHERE a.id = #{value}
  14.     ]]>
  15. </select>
测试用例
  1. com.javacode2018.chat05.demo7.Demo7Test#getById8
  2. @Test
  3. public void getById8() throws IOException {
  4.     this.before("demo7/mybatis-config.xml");
  5.     try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
  6.         OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
  7.         OrderModel orderModel = mapper.getById8(1);
  8.         log.info("{}", orderModel);
  9.     }
  10. }
运行输出
  1. 11:06.193 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById8 - ==>  Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime FROM t_order a WHERE a.id = ? 
  2. 11:06.229 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById8 - ==> Parameters: 1(Integer)
  3. 11:06.250 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - ====>  Preparing: SELECT a.id, a.order_id AS orderId, a.goods_id AS goodsId, a.num, a.total_price AS totalPrice FROM t_order_detail a WHERE a.order_id = ? 
  4. 11:06.251 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - ====> Parameters: 1(Integer)
  5. 11:06.255 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - <====      Total: 2
  6. 11:06.256 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById8 - <==      Total: 1
  7. 11:06.256 [main] INFO  c.j.chat05.demo7.Demo7Test - OrderModel(id=null, userId=2, createTime=1577947790, upTime=1577947790, userModel=null, orderDetailModelList=[OrderDetailModel(id=1, orderId=1, goodsId=1, num=2, totalPrice=17.76), OrderDetailModel(id=2, orderId=1, goodsId=1, num=1, totalPrice=16.66)])

注意输出中OrderModel的id属性,怎么是null值?主要是下面这行配置导致的

<collection property="orderDetailModelList" select="com.javacode2018.chat05.demo7.mapper.OrderDetailMapper.getListByOrderId1" column="id"/>

上面这个配置中有个column属性,指定的是id,此时mybatis认为你对id字段手动指定了映射关系,就跳过了对id字段到OrderModel.id属性的自动映射,所以导致OrderModel对象的id属性没有赋值,此时需要我们在orderModelMap8手动指定id的映射规则,如下:

  1. <resultMap id="orderModelMap8" type="com.javacode2018.chat05.demo7.model.OrderModel" autoMapping="true">
  2.     <id column="id" property="id" />
  3.     <collection property="orderDetailModelList" select="com.javacode2018.chat05.demo7.mapper.OrderDetailMapper.getListByOrderId1" column="id"/>
  4. </resultMap>

再去运行测试用例就正常了。

总结一下

对于咱们开发来说,自动映射确实可以帮助我们节省一些代码,不过也存在一些隐患,我们希望自己开发的系统是健壮的,建议大家写mapper xml的时候,还是花点时间将映射的配置都给写上去,这样能够杜绝一些隐患,使我们的系统更稳定。

MyBatis系列

  1. MyBatis系列第1篇:MyBatis未出世之前我们那些痛苦的经历

  2. MyBatis系列第2篇:入门篇,带你感受一下mybatis独特的魅力!

  3. MyBatis系列第3篇:Mybatis使用详解(1)

  4. MyBatis系列第4篇:Mybatis使用详解(2)

  5. Mybatis系列第5篇:Mapper接口多种方式传参详解、原理、源码解析

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

  7. Mybatis系列第7篇:各种查询详解

更多好文章

  1. Java高并发系列(共34篇)

  2. MySql高手系列(共27篇)

  3. Maven高手系列(共10篇)

  4. 聊聊db和缓存一致性常见的实现方式

  5. 接口幂等性这么重要,它是什么?怎么实现?

感谢大家的阅读,也欢迎您把这篇文章分享给更多的朋友一起阅读!谢谢!

路人甲java

▲长按图片识别二维码关注

路人甲Java:工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!来源:https://itsoku.blog.csdn.net/article/details/103825881

posted @ 2022-04-23 03:12  程序员小明1024  阅读(289)  评论(0编辑  收藏  举报