文档章节

Mybatis3.3.x技术内幕(十五):Mybatis之foreach批量insert,返回主键id列表(修复Mybatis返回null的bug)

祖大俊
 祖大俊
发布于 2016/05/13 22:47
字数 2258
阅读 12483
收藏 103
点赞 13
评论 17

Mybatis在执行批量插入时,如果使用的是for循环逐一插入,那么可以正确返回主键id。如果使用动态sql的foreach循环,那么返回的主键id列表,可能为null,这让很多人感到困惑;本文将分析问题产生的原因,并修复返回主键id为null的问题。该问题在开源中国社区,以及网络上,已经有很多人遇到并发帖咨询,似乎都没有得到期望的解决结果。今天,我将带领大家,分析并解决该问题,让foreach批量插入,返回正确的id列表。

	<insert id="insertStudents" useGeneratedKeys="true" keyProperty="studId" parameterType="java.util.ArrayList">
		INSERT INTO
		STUDENTS(STUD_ID, NAME, EMAIL, DOB, PHONE)
		VALUES
	<foreach collection="list" item="item" index="index" separator=","> 
        	(#{item.studId},#{item.name},#{item.email},#{item.dob}, #{item.phone}) 
    	</foreach> 
	</insert>

以上便是Mybatis的foreach循环,其要生成的sql语句是:insert into students(stud_id, name) values(?, ?),(?, ?), (?, ?); 类似这样的批量插入。

Mybatis是对Jdbc的封装,我们来看看,Jdbc是否支持上述形式的批量插入,并返回主键id列表的。

PreparedStatement pstm = conn.prepareStatement("insert into students(name, email) values(?, ?), (?, ?), (?, ?)",
				Statement.RETURN_GENERATED_KEYS);

pstm.setString(1, "name1");
pstm.setString(2, "email1");


pstm.setString(3, "name2");
pstm.setString(4, "email2");
		
pstm.setString(5, "name2");
pstm.setString(6, "email2");

pstm.addBatch();
pstm.executeBatch();

ResultSet rs = pstm.getGeneratedKeys();
while (rs.next()) {
	Object value = rs.getObject(1);
	System.out.println(value);
}

Output:

248
249
250

好了,事实证明,Jdbc是支持上述批量插入,并能正确返回id列表的。Jdbc都支持,如果Mybatis却不支持,有点说不过去。

1. Mapper.xml中keyProperty和parameterType属性之间的关系(很重要)

useGeneratedKeys="true" keyProperty="studId" parameterType="Student"

上述xml配置,含义为,属性studId是参数类型Student对象的主键属性。毫无疑问,Student对象中有studId属性。

useGeneratedKeys="true" keyProperty="studId" parameterType="java.util.ArrayList"

那这个如何解释呢?ArrayList有studId属性吗?当然没有了。其正确含义为:ArrayList集合中的元素的studId属性。

所以,keyProperty和parameterType之间的关系,有时是直接关系,有时是间接关系。明白这个道理之后,我们就可以开始进一步阅读源码了。

2. Mybatis对parameter object的解析

org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator.java源码(只保留了重点源码)

  @Override
  public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    processBatch(ms, stmt, getParameters(parameter));
  }

  public void processBatch(MappedStatement ms, Statement stmt, Collection<Object> parameters) {
    ResultSet rs = null;
    try {
      rs = stmt.getGeneratedKeys();
        // 迭代出来的对象parameter,一定要具备keyProperty属性
        for (Object parameter : parameters) {
          metaParam.setValue(keyProperties, value);
        }
      }
    }
  }

  private Collection<Object> getParameters(Object parameter) {
    Collection<Object> parameters = null;
    if (parameter instanceof Collection) {
      // 集合
      parameters = (Collection) parameter;
    } else if (parameter instanceof Map) {
      // map
      Map parameterMap = (Map) parameter;
      if (parameterMap.containsKey("collection")) {
        parameters = (Collection) parameterMap.get("collection");
      } else if (parameterMap.containsKey("list")) {
        parameters = (List) parameterMap.get("list");
      } else if (parameterMap.containsKey("array")) {
        parameters = Arrays.asList((Object[]) parameterMap.get("array"));
      }
    }
    if (parameters == null) {
      parameters = new ArrayList<Object>();
      parameters.add(parameter);
    }
    return parameters;
  }

上面这段代码,非常关键且重要,特别是我做了注释的地方,for(Object parameter : parameters)循环,表示parameters一定是一个集合,如果传递的是Student对象,那么Mybatis会将其封装到List<Student>中,然后再进行迭代操作。于是,迭代出来的parameter就是Student对象,就具备了keyProperty指定的属性了,比如studId属性。

如果传递的是一个List<Student>呢?

org.apache.ibatis.session.defaults.DefaultSqlSession.wrapCollection(Object)源码。

executor.update(ms, wrapCollection(parameter));
// ...
  private Object wrapCollection(final Object object) {
    // 如果是集合,再度包装为Map对象
    if (object instanceof Collection) {
      StrictMap<Object> map = new StrictMap<Object>();
      map.put("collection", object);
      if (object instanceof List) {
        map.put("list", object);
      }
      return map;
    } else if (object != null && object.getClass().isArray()) {
      // 数组
      StrictMap<Object> map = new StrictMap<Object>();
      map.put("array", object);
      return map;
    }
    return object;
  }

上面这段代码也非常重要,如果传递的是List<Student>,那么,将包装为一个Map<String, Collection>对象。

于是,List<Student>形式的parameter object就变成了下面这个样子,一个Map<String, List<Student>>对象,Map的size()为2,key分别为“collection”和“list”。下面会经常用到这个Map<String, List<Student>>对象,所以,要记住其数据结构。

{
    collection=[
        com.mybatis3.domain.Student@2d2ffcb7,
        com.mybatis3.domain.Student@762ef0ea
    ],
    list=[
        com.mybatis3.domain.Student@2d2ffcb7,
        com.mybatis3.domain.Student@762ef0ea
    ]
}

因此,Mybatis将集合类参数对象,包装成上面的一个Map<String, List<Student>>结构了。明白了数据的组织结构,就可以进行下一步的分析了。

3. SimpleExecutor和ReuseExecutor可以正确返回foreach批量插入后的id列表的原理

还记得如何配置Executor吗?

<setting name="defaultExecutorType" value="SIMPLE" />

既然集合参数,已经被包装成了Map<String, List<Student>>对象,当然就无法使用for(Object parameter : parameters)来迭代Map<String, List<Student>>了,我们看看SimpleExecutor和ReuseExecutor是如何做到的。

  private Collection<Object> getParameters(Object parameter) {
    Collection<Object> parameters = null;
    if (parameter instanceof Collection) {
      parameters = (Collection) parameter;
    } else if (parameter instanceof Map) {
      Map parameterMap = (Map) parameter;
      if (parameterMap.containsKey("collection")) {
        // 返回map中key=collection的value
        parameters = (Collection) parameterMap.get("collection");
      } else if (parameterMap.containsKey("list")) {
        // 返回map中key=list的value
        parameters = (List) parameterMap.get("list");
      } else if (parameterMap.containsKey("array")) {
        parameters = Arrays.asList((Object[]) parameterMap.get("array"));
      }
    }
    if (parameters == null) {
      parameters = new ArrayList<Object>();
      parameters.add(parameter);
    }
    return parameters;
  }

getParameters()方法,会再次处理参数类型,前面是包装,这里是拆封,于是,无论返回上面的哪一个value,都是List<Student>或Collection集合,于是就可以使用for(Object parameter : parameters)来迭代,迭代出来的parameter就是Student,Student的主键属性为keyProperty。

结论:使用SimpleExecutor和ReuseExecutor,执行foreach批量插入,可以正确返回主键id列表。

然而,很可惜,BatchExecutor却存在bug,返回主键id列表为null值。

4. BatchExecutor执行foreach批量插入,返回主键id列表为null的原因以及如何修复

每当提到批量插入,同学们总是自然而然的想到BatchExecutor,这是程序员的本能。就像一想到交女朋友,就想到美女是一样的道理。

BatchExecutor使用了一个BatchResult对象,来保存执行参数以及执行结果。

org.apache.ibatis.executor.BatchResult.java源码。

public class BatchResult {

  private final List<Object> parameterObjects;
  // 居然不建议使用了
  @Deprecated
  public Object getParameterObject() {
    return parameterObjects.get(0);
  }
  // 直接返回List<map>对象
  public List<Object> getParameterObjects() {
    return parameterObjects;
  }

  // 将parameterObject放到List中
  public void addParameterObject(Object parameterObject) {
    this.parameterObjects.add(parameterObject);
  }

前面已经讲述了,List<Student>,被包装为Map<String, List<Student>>对象了,BatchResult又把Map<String, List<Student>>放到List中,于是,参数对象数据结构就变成了List<Map<String, List<Student>>>。

org.apache.ibatis.executor.BatchExecutor.doFlushStatements()方法源码。

Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);

此时的parameterObjects对象,已经是List<Map<String, List<Student>>>对象了,再执行for(Object parameter : parameterObjects)迭代,迭代出来的parameter是Map<String, List<Student>>对象,Map<String, List<Student>>对象当然没有keyProperty指定的属性了,期望迭代出来的目标对象是Student,而不是Map。于是,就产生了错误。由于不能正确赋值,自然就无法将主键id值,赋值给Student对象的主键属性studId了,所以返回主键id值null,大家就认为是Mybatis不支持,其实是个误会。

自己动手,修复该问题(修改BatchExecutor.doFlushStatements()方法源码):

//Mybaits源码
//jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);

//修复后代码
jdbc3KeyGenerator.processBatch(ms, stmt, this.getParameters(batchResult.getParameterObject()));

// org.apache.ibatis.executor.BatchExecutor中手动新增下面这个方法
public Collection<Object> getParameters(Object parameter) {
    Collection<Object> parameters = null;
    if (parameter instanceof Collection) {
      parameters = (Collection) parameter;
    } else if (parameter instanceof Map) {
      Map parameterMap = (Map) parameter;
      if (parameterMap.containsKey("collection")) {
        parameters = (Collection) parameterMap.get("collection");
      } else if (parameterMap.containsKey("list")) {
        parameters = (List) parameterMap.get("list");
      } else if (parameterMap.containsKey("array")) {
        parameters = Arrays.asList((Object[]) parameterMap.get("array"));
      }
    }
    if (parameters == null) {
      parameters = new ArrayList<Object>();
      parameters.add(parameter);
    }
    return parameters;
  }

解释一下上面的代码:

1. batchResult.getParameterObject()返回List<Map<String, List<Student>>>中的第0个元素(List长度本身就是1),于是得到Map<String, List<Student>>对象。

2. getParameters(map)方法拆封,返回map的任一value对象,该value对象就是原始的List<Student>对象。该方法本是org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator内的一个private方法,在外面不能调用,于是,复制一份出来,放到BatchExecutor中来使用。

3. for(Object parameter : parameters)迭代后,parameter就是Student元素,该元素有主键属性studId,于是把数据库返回的主键id值,赋给sutdId属性。

经过以上三个步骤,我们的BatchExecutor就可以通过foreach批量插入,正确返回id列表了。

至此,SimpleExecutor、ReuseExecutor、BatchExecutor,均可以执行foreach批量插入,并正确返回id列表了。直接修改源代码,有点暴力,后续讲到plugin拦截器时,可以再看看,有没有更优雅的方式。

 

注:我不清楚Mybatis为何要这么设计,这究竟真是一个bug,还是Mybatis故意为之,只有时间能给出答案了。

 

版权提示:文章出自开源中国社区,若对文章感兴趣,可关注我的开源中国社区博客(http://my.oschina.net/zudajun)。(经过网络爬虫或转载的文章,经常丢失流程图、时序图,格式错乱等,还是看原版的比较好)

 

© 著作权归作者所有

共有 人打赏支持
祖大俊
粉丝 612
博文 32
码字总数 52477
作品 0
昌平
加载中

评论(17)

QinRoc
QinRoc
感谢。
这篇文章解决了我的问题。
<setting name="defaultExecutorType" value="SIMPLE" />
之前想当然的配置成BATCH模式,结果Github上给出的方案也报错......
改成SIMPLE模式就可以了。
Mr_Qi
Mr_Qi

引用来自“thomasyanglin”的评论

@祖大俊 关于批量插入
楼主,我怎么觉得虽然官方issue中已经解决了这个问题,但是当executor type 配置成batch的时候,还是会出现问题,如下是我尝试的时候出现的问题
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [collection, list]
batch的时候就是有问题的啊~之前是连reuse也不行的。

https://my.oschina.net/qixiaobo025/blog/1501633
thomasyanglin
thomasyanglin
@祖大俊 关于批量插入
楼主,我怎么觉得虽然官方issue中已经解决了这个问题,但是当executor type 配置成batch的时候,还是会出现问题,如下是我尝试的时候出现的问题
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [collection, list]
rmqc0909
rmqc0909

引用来自“rmqc0909”的评论

我重新评论一条吧,刚才的回复忽视啊:
xml代码:
<insert id="insertList" useGeneratedKeys="true" keyProperty="id" parameterType="java.util.List">
INSERT INTO country (countryname,countrycode )
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.countryName},#{item.countryCode})
</foreach>
</insert>

引用来自“祖大俊”的评论

我在本地完全复现了你的异常,你只要按照我下面回复的去修改,就可以解决,这是mybatis的bug。
Collection getParameters(Object parameter) 方法是Jdbc3KeyGenerator内的一个private方法,在外面不能使用,所有,复制一份放到org.apache.ibatis.executor.BatchExecutor来使用。

产生bug的原理,在这篇文章中有详细描述,或者你可以自己debug一下,你就懂了。
我看懂你针对BATCH的修改方法了,我对于一级二级缓存的疑问,我自己理解了,就是说mybatis默认开启一级缓存,不论是否开启二级缓存,mybatis进去时都会判断
org.apache.ibatis.session.Configuration
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
这里面的cacheEnabled字段应该指的是一级缓存吧。
所以只要cacheEnabled值为true,都会去new一个CachingExecutor,到真正query时,再去判断二级缓存中有没有值,若没有,则去数据库查询。是这样吧。。
rmqc0909
rmqc0909
我在粘一下我对缓存设置的相关代码:
conf.xml里面
<settings>
<!-- 将数据库字段命名规则A_COLUMN转换为Java使用的驼峰式命名规则aCloumn -->
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
mapper.xml里面:
<mapper namespace="com.github.mapping.User">
<select id="getByID" parameterType="int" resultType="User">
SELECT user_id, user_name, user_password, nick_name, email, is_valid, created_time, update_time
FROM sys_user WHERE user_id = #{id}
</select>
测试:
@Test
public void testGetById() {
SqlSession sqlSession = sqlSessionFactory.openSession();
SysUser sysUser;
try {
sysUser = sqlSession.selectOne("com.github.mapping.User.getByID", 3);
if (sysUser == null) {
System.out.println("the result is null.");
}
else {
System.out.println(sysUser);
}
} finally {
sqlSession.close
}
}



rmqc0909
rmqc0909

引用来自“rmqc0909”的评论

我重新评论一条吧,刚才的回复忽视啊:
xml代码:
<insert id="insertList" useGeneratedKeys="true" keyProperty="id" parameterType="java.util.List">
INSERT INTO country (countryname,countrycode )
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.countryName},#{item.countryCode})
</foreach>
</insert>

引用来自“祖大俊”的评论

我在本地完全复现了你的异常,你只要按照我下面回复的去修改,就可以解决,这是mybatis的bug。
Collection getParameters(Object parameter) 方法是Jdbc3KeyGenerator内的一个private方法,在外面不能使用,所有,复制一份放到org.apache.ibatis.executor.BatchExecutor来使用。

产生bug的原理,在这篇文章中有详细描述,或者你可以自己debug一下,你就懂了。
谢谢楼主,我自己debug试一下,还有个问题哈,就是看你的倒数第二篇讲的一级二级缓存,我本地没有开启二级缓存,可是我debug这个testGetById()方法,发现进去以后的Executors是CachingExecutor,我之前肯定junit过这个方法,但是我没有设置二级缓存,它默认的二级缓存不是false吗?官网也是这么说的:
引自mybatis官网:
默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环 依赖也是必须的。要开启二级缓存,你需要在你的 SQL 映射文件中添加一行
祖大俊
祖大俊

引用来自“rmqc0909”的评论

我重新评论一条吧,刚才的回复忽视啊:
xml代码:
<insert id="insertList" useGeneratedKeys="true" keyProperty="id" parameterType="java.util.List">
INSERT INTO country (countryname,countrycode )
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.countryName},#{item.countryCode})
</foreach>
</insert>
我在本地完全复现了你的异常,你只要按照我下面回复的去修改,就可以解决,这是mybatis的bug。
Collection getParameters(Object parameter) 方法是Jdbc3KeyGenerator内的一个private方法,在外面不能使用,所有,复制一份放到org.apache.ibatis.executor.BatchExecutor来使用。

产生bug的原理,在这篇文章中有详细描述,或者你可以自己debug一下,你就懂了。
祖大俊
祖大俊

引用来自“rmqc0909”的评论

我重新评论一条吧,刚才的回复忽视啊:
xml代码:
<insert id="insertList" useGeneratedKeys="true" keyProperty="id" parameterType="java.util.List">
INSERT INTO country (countryname,countrycode )
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.countryName},#{item.countryCode})
</foreach>
</insert>
异常我完全还原了,你按照我给的方案,自己修改一下,就OK了,这是mybatis的bug,你需要修改源码。
1、org.apache.ibatis.executor.BatchExecutor.doFlushStatements(boolean)
修改源码:
// mybatis源码
//jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);

// 修改为下面的
jdbc3KeyGenerator.processBatch(ms, stmt, this.getParameters(batchResult.getParameterObject()));
2、还是这个类中,加入方法。
Collection getParameters(Object parameter) {
   Collection parameters = null;
   if (parameter instanceof Collection) {
   parameters = (Collection) parameter;
   } else if (parameter instanceof Map) {
   Map parameterMap = (Map) parameter;
   if (parameterMap.containsKey("collection")) {
   parameters = (Collection) parameterMap.get("collection");
   } else if (parameterMap.containsKey("list")) {
   parameters = (List) parameterMap.get("list");
   } else if (parameterMap.containsKey("array")) {
   parameters = Arrays.asList((Object[]) parameterMap.get("array"));
   }
   }
   if (parameters == null) {
   parameters = new ArrayList();
   parameters.add(parameter);
   }
   return parameters;
   }

两个步骤以后,就OK了。我亲自复现问题,并解决问题的。
rmqc0909
rmqc0909
我重新评论一条吧,刚才的回复忽视啊:
xml代码:
<insert id="insertList" useGeneratedKeys="true" keyProperty="id" parameterType="java.util.List">
INSERT INTO country (countryname,countrycode )
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.countryName},#{item.countryCode})
</foreach>
</insert>
rmqc0909
rmqc0909

引用来自“rmqc0909”的评论

楼主你好,我用的是3.4.2版本,用ExecutorType.BATCH执行器,返回主键id报错如下:Error getting generated key or setting result to parameter object. Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [collection, list],但是用其他两个执行器可以返回正确的主键id;
测试代码如下:
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
try {
CountryMapper countryMapper = sqlSession.getMapper(CountryMapper.class);
List<Country> countries = new ArrayList<>();
Country country = new Country(null,"cn988","cc1");
countries.add(country);
Country country2 = new Country(null,"cn988","cc2");
countries.add(country2);
Country country3 = new Country(null,"cn988","cc3");
countries.add(country3);
int result = countryMapper.insertList(countries);
sqlSession.commit();
} finally {
sqlSession.close();
}

引用来自“祖大俊”的评论

这篇文章,说的就是ExecutorType.BATCH执行器,在执行批量insert时,不能正确返回id主键列表的问题。所以,你的实验结果很正常。
这样,你把你的insertList的xml配置也贴出来,我在我电脑上调试一下,我本机用的是mysql数据库。
我也用的mysql,下面是我的xml语句:

INSERT INTO country (countryname,countrycode )
VALUES

(#{item.countryName},#{item.countryCode})

Mybatis3.3.x技术内幕(十四):Mybatis之KeyGenerator

在Mybatis中,执行insert操作时,如果我们希望返回数据库生成的自增主键值,那么就需要使用到KeyGenerator对象。 需要注意的是,KeyGenerator的作用,是返回数据库生成的自增主键值,而不是生...

祖大俊
2016/05/11
890
3
Mybatis3.4.x技术内幕(十六):Mybatis之sqlFragment(可复用的sql片段)

Mybatis目前最新版本为3.4.0,因此,我也将我的项目由3.3.1替换为3.4.0。在上一篇博文中,详细分析了Mybatis在使用foreach循环进行批量insert,返回主键id列表时,如果使用BatchExecutor,那...

祖大俊
2016/06/05
998
0
mybatis中mysql和oracle的区别

mysql和oracle语法有一定的差异,我们将服务由部署mysql的服务器迁移到部署oracle的服务器上时,需要修改sql语句。下面说说mybatis中由mysql转为oracle需要修改的语句。 1.批量插入mysql: ...

sietai
05/18
0
0
Mybatis3.4.x技术内幕(二十三):Mybatis面试问题集锦(大结局)

Mybatis技术内幕系列博客,从原理和源码角度,介绍了其内部实现细节,无论是写的好与不好,我确实是用心写了,由于并不是介绍如何使用Mybatis的文章,所以,一些参数使用细节略掉了,我们的目...

祖大俊
2016/09/17
10.9K
34
Mybatis[批量]插入返回自增ID

简介 最近在业务功能中需要获取mybatis插入的数据并且返回插入数据的ID,去执行其他的操作,说来也很简单,在正常的insert标签里面加入提供的其他属性即可实现,故现在抽时间整理出来,希望能...

阿郎_
2017/05/23
0
0
Mybatis-select、insert、update、delete标签详解

<selectid="selectPerson"parameterType="int"parameterMap="deprecated"resultType="hashmap"resultMap="personResultMap"flushCache="false"useCache="true"timeout="10000"fetchSize="256......

Romanceling
2016/10/11
2.1K
0
【MyBatis框架】MyBatis入门程序第二部分

我们通过写一个简单的MyBatis小项目来在实战中学习MyBatis,接着上一篇继续 我们开始实现需求中的添加和删除用户功能 (1)向数据库中添加用户数据 使用User.xml,加入添加用户的sql语句。 <!--...

Mysoft
2015/09/17
39
0
mybatis获取当前插入记录的id

问题:mybatis会自动生成一个insert方法如(用的是MySQL数据库): <insert id="insert" parameterType="cn.hnne.iclt.model.Task" > <selectKey resultType="java.lang.Integer" keyProperty=......

sky清水无香sky
2013/09/06
0
4
mybaitis批量插入怎么得到批量返回的自增ID

各位大侠你们好。。如题。。求解,下面是DAO和SQL: spring Dao的方法定义: public List saveCatalogList(@Param("catalist") List catalist); mybatis的批量插入SQL: insert into CATALO...

心心相印
2015/10/24
1K
2
MySQL批量插入返回自增ID的问题

MySQL批量插入返回自增ID的问题 Mz的博客2017-08-1838 阅读 数据库MySQL 业务场景 现需将表A中的数据经过转换后迁移入表B,并将一些附加数据存入表C,表C通过一个BId字段来与表B中的数据进行...

Mz的博客
2017/08/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Hbase增删查改工具类

package cn.hljmobile.tagcloud.service.data.repository;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util......

gulf
6分钟前
0
0
详解机器学习中的梯度消失、爆炸原因及其解决方法

前言 本文主要深入介绍深度学习中的梯度消失和梯度爆炸的问题以及解决方案。本文分为三部分,第一部分主要直观的介绍深度学习中为什么使用梯度更新,第二部分主要介绍深度学习中梯度消失及爆...

tantexian
7分钟前
0
0
JavaMail 发送邮件

参考 https://www.cnblogs.com/xdp-gacl/p/4216311.html 发送html格式邮件 package com.example.stumgr;import java.util.Properties;import javax.mail.Message;import javax.mail......

阿豪boy
9分钟前
0
0
Mongodb安装教程

MongoDB是一个基于分布式文件存储的数据库,是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bso...

木筏笔歆
10分钟前
0
0
Hadoop之YARN命令

概述 YARN命令是调用bin/yarn脚本文件,如果运行yarn脚本没有带任何参数,则会打印yarn所有命令的描述。 使用: yarn [--config confdir] COMMAND [--loglevel loglevel] [GENERIC_OPTIONS] [...

舒运
11分钟前
0
0
个推数据统计产品(个数)iOS集成实践

最近业务方给我们部门提了新的需求,希望能一站式统计APP的几项重要数据。这次我们尝试使用的是个推(之前专门做消息推送的)旗下新推出的产品“个数·应用统计”,根据官方的说法,个推的数...

个推
12分钟前
0
0
Git 修改提交的用户名和邮箱名字

在通过git提交代码时,发现提交的用户名是自己mac的账户名,想要修改为其他名字和邮箱。 首先可以通过以下命令查看当前配置下的信息,包括用户名和邮箱: > git config --list 针对单项目的相...

edwardGe
15分钟前
0
0
Object.defineProperty()

Object.defineProperty(obj, props)方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。 obj 在其上定义或修改属性的对象 props 要定义其可枚举属性或修改的属性描述符的对象 ...

litCabbage
17分钟前
0
0
JEESZ分布式框架--单点登录集成方案(三)

多项目集成单点登录配置 当sso验证完成之后,客户端系统需要接收sso系统返回的结果时,需要定义一个过滤器获取返回结果,然后针对返回结果做相关处理.如果不需要做处理时,此处Filter也可以不...

明理萝
17分钟前
0
1
超简单的利用plist 查看ipa包名及其它信息

1.下载ipa安装包 2.用rar等工具打开 3.将iTunesMetadata.plist文件解压出来 4.用http://www.atool.org/plist_reader.php在线反编译工具 5.在其中中找到softwareVersionBundleId 就是包名...

xiaogg
18分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部