MyBatis主键#

不支持对象列表存储时对自增id字段的赋值(至少包括3.2.6和3.3.0版本),如果id不是采用底层DB自增主键赋值,不必考虑此问题

温馨提示:分布式DB环境下,DB主键一般会采用统一的Id生成器生成Id,因此不必考虑由数据库自增策略填充主键值。

解决方案#

参考源码##

1)mybatis-batch-insert项目,请为原作者点赞,支持他开源

备注:实际代码有少量修改,会在下文列出,本文依据实现方案代码细节反推分析源码处理逻辑过程

批量插入对象列表自增主键赋值分析##

1)在获取数据库返回的主键值后填充到中间存储结构。

2)在构造具体返回对象结构过程中(其实insert语句并不需要),从中间存储结构将多个主键值填充到具体的对象实例当中。

备注:实际上这种解决方案还是来源于代码分析的结果,接下来简单列述Mybatis主键处理的核心代码及配置

MyBatis处理主键处理流程#

备注:主键填充的处理方法实际是populateKeys。

代码呈上#

测试示例

  1. package org.wit.ff.jdbc;
  2. import org.junit.Test;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.test.context.ContextConfiguration;
  5. import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
  6. import org.wit.ff.jdbc.dao.HomeTownDao;
  7. import org.wit.ff.jdbc.id.BatchInsertEntities;
  8. import org.wit.ff.jdbc.model.HomeTown;
  9. import org.wit.ff.jdbc.query.Criteria;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. /**
  13. * Created by F.Fang on 2015/11/17.
  14. * Version :2015/11/17
  15. */
  16. @ContextConfiguration(locations = {"classpath:applicationContext-batch.xml"})
  17. public class HomeTownDaoBatchTest extends AbstractJUnit4SpringContextTests {
  18. @Autowired
  19. private HomeTownDao homeTownDao;
  20. @Test
  21. public void testBatchInsert(){
  22. HomeTown ht1 = new HomeTown();
  23. ht1.setName("hb");
  24. ht1.setLocation("hubei");
  25. HomeTown ht2 = new HomeTown();
  26. ht2.setName("js");
  27. ht2.setLocation("jiangsu");
  28. List<HomeTown> list = new ArrayList<>();
  29. list.add(ht1);
  30. list.add(ht2);
  31. BatchInsertEntities<HomeTown> batchEntities = new BatchInsertEntities<>(list);
  32. homeTownDao.batchInsert(batchEntities);
  33. System.out.println(batchEntities.getEntities());
  34. }
  35. }

控制台输出

  1. [3,hb,hubei, 4,js,jiangsu]

模型HomeTown

  1. package org.wit.ff.jdbc.model;
  2. import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
  3. import org.apache.commons.lang3.builder.ToStringStyle;
  4. import org.wit.ff.jdbc.id.IdGenerator;
  5. /**
  6. * Created by F.Fang on 2015/11/17.
  7. * Version :2015/11/17
  8. */
  9. public class HomeTown implements IdGenerator {
  10. private int id;
  11. private String name;
  12. private String location;
  13. public int getId() {
  14. return id;
  15. }
  16. public void setId(int id) {
  17. this.id = id;
  18. }
  19. public String getName() {
  20. return name;
  21. }
  22. public void setName(String name) {
  23. this.name = name;
  24. }
  25. public String getLocation() {
  26. return location;
  27. }
  28. public void setLocation(String location) {
  29. this.location = location;
  30. }
  31. public String toString() {
  32. return ReflectionToStringBuilder.toString(this, ToStringStyle.SIMPLE_STYLE);
  33. }
  34. @Override
  35. public void parseGenKey(Object[] value) {
  36. if(value!=null && value.length == 1){
  37. this.id = Integer.valueOf(value[0].toString());
  38. }
  39. }
  40. }

HomeTownDao

  1. package org.wit.ff.jdbc.dao;
  2. import org.wit.ff.jdbc.id.BatchInsertEntities;
  3. import org.wit.ff.jdbc.model.HomeTown;
  4. import java.util.List;
  5. /**
  6. * Created by F.Fang on 2015/11/17.
  7. * Version :2015/11/17
  8. */
  9. public interface HomeTownDao {
  10. void batchInsert(BatchInsertEntities<HomeTown> batchEntities);
  11. }

Mapper配置文件

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="org.wit.ff.jdbc.dao.HomeTownDao">
  6. <insert id="batchInsert" parameterType="org.wit.ff.jdbc.id.BatchInsertEntities" useGeneratedKeys="true" keyProperty="id"
  7. keyColumn="ID">
  8. insert into hometown
  9. (name,location)
  10. values
  11. <foreach item="item" collection="entities" separator=",">
  12. ( #{item.name},#{item.location})
  13. </foreach>
  14. </insert>
  15. </mapper>

Spring配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  6. http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
  7. <!-- 数据源 -->
  8. <bean id="dataSource"
  9. class="org.apache.commons.dbcp.BasicDataSource"
  10. destroy-method="close">
  11. <property name="driverClassName" value="${db.driverClass}"/>
  12. <property name="url" value="${db.jdbcUrl}"/>
  13. <property name="username" value="${db.user}"/>
  14. <property name="password" value="${db.password}"/>
  15. </bean>
  16. <!-- 配置 SqlSessionFactory -->
  17. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  18. <property name="dataSource" ref="dataSource"/>
  19. <!-- 制定路径自动加载mapper配置文件 -->
  20. <property name="mapperLocations" value="classpath:mappers/*Dao.xml"/>
  21. <!-- 配置myibatis的settings http://mybatis.github.io/mybatis-3/zh/configuration.html#settings -->
  22. <property name="configurationProperties">
  23. <props>
  24. <prop key="cacheEnabled">true</prop>
  25. </props>
  26. </property>
  27. <property name="typeHandlers">
  28. <list>
  29. <bean class="org.wit.ff.jdbc.id.BatchInsertEntitiesTypeHandler"/>
  30. </list>
  31. </property>
  32. <property name="objectWrapperFactory" ref="batchObjectWrapperFactory"/>
  33. <!-- 类型别名是为 Java 类型命名一个短的名字。 它只和 XML 配置有关, 只用来减少类完全 限定名的多余部分 -->
  34. <property name="typeAliasesPackage" value="org.wit.ff.jdbc.model"/>
  35. </bean>
  36. <bean id="batchObjectWrapperFactory" class="org.wit.ff.jdbc.id.BatchInsertObjectWrapperFactory"/>
  37. <mybatis:scan base-package="org.wit.ff.jdbc.dao"/>
  38. </beans>

存储主键值的结构

  1. package org.wit.ff.jdbc.id;
  2. import java.util.List;
  3. public class BatchInsertEntityPrimaryKeys {
  4. private final List<String> primaryKeys;
  5. public BatchInsertEntityPrimaryKeys(List<String> pks) {
  6. this.primaryKeys = pks;
  7. }
  8. public List<String> getPrimaryKeys() {
  9. return primaryKeys;
  10. }
  11. }

批量对象列表包装

  1. package org.wit.ff.jdbc.id;
  2. import java.util.List;
  3. public class BatchInsertEntities<T extends IdGenerator> {
  4. private final List<T> entities;
  5. public BatchInsertEntities(List<T> entities) {
  6. this.entities = entities;
  7. }
  8. /**
  9. * <p>
  10. * The entities will be batch inserted into DB. The entities are also the
  11. * parameters of the
  12. * {@link org.apache.ibatis.binding.MapperMethod.SqlCommand}.
  13. */
  14. public List<T> getEntities() {
  15. return entities;
  16. }
  17. }

自定义TypeHandler

  1. package org.wit.ff.jdbc.id;
  2. import java.sql.CallableStatement;
  3. import java.sql.PreparedStatement;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.util.LinkedList;
  7. import java.util.List;
  8. import org.apache.ibatis.type.BaseTypeHandler;
  9. import org.apache.ibatis.type.JdbcType;
  10. public class BatchInsertEntitiesTypeHandler extends BaseTypeHandler<BatchInsertEntityPrimaryKeys> {
  11. public BatchInsertEntityPrimaryKeys getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
  12. // Read the primary key values from result set. It is believed that
  13. // there is 1 primary key column.
  14. List<String> pks = new LinkedList<>();
  15. do {
  16. // rs.next is called before.
  17. pks.add(rs.getString(columnIndex));
  18. } while (rs.next());
  19. return new BatchInsertEntityPrimaryKeys(pks);
  20. }
  21. @Override
  22. public void setNonNullParameter(PreparedStatement ps, int i, BatchInsertEntityPrimaryKeys parameter,
  23. JdbcType jdbcType) throws SQLException {
  24. // TODO Auto-generated method stub
  25. //System.out.println(" BatchInsertEntitiesTypeHandler#setNonNullParameter got called. ");
  26. }
  27. @Override
  28. public BatchInsertEntityPrimaryKeys getNullableResult(ResultSet rs, String columnName) throws SQLException {
  29. // TODO Auto-generated method stub
  30. //System.out.println(" BatchInsertEntitiesTypeHandler#getNullableResult got called. ");
  31. return null;
  32. }
  33. @Override
  34. public BatchInsertEntityPrimaryKeys getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
  35. // TODO Auto-generated method stub
  36. //System.out.println(" BatchInsertEntitiesTypeHandler#getNullableResult got called. ");
  37. return null;
  38. }
  39. }

自定义ObjectWrapper

  1. package org.wit.ff.jdbc.id;
  2. import java.util.Iterator;
  3. import java.util.List;
  4. import org.apache.ibatis.reflection.MetaObject;
  5. import org.apache.ibatis.reflection.factory.ObjectFactory;
  6. import org.apache.ibatis.reflection.property.PropertyTokenizer;
  7. import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
  8. /**
  9. * Wrap the collection object for batch insert.
  10. * https://github.com/jactive/java
  11. */
  12. public class BatchInsertObjectWrapper implements ObjectWrapper {
  13. private final BatchInsertEntities<IdGenerator> entity;
  14. public BatchInsertObjectWrapper(MetaObject metaObject, BatchInsertEntities<IdGenerator> object) {
  15. this.entity = object;
  16. }
  17. @Override
  18. public void set(PropertyTokenizer prop, Object value) {
  19. // check the primary key type existed or not when setting PK by reflection.
  20. BatchInsertEntityPrimaryKeys pks = (BatchInsertEntityPrimaryKeys) value;
  21. if (pks.getPrimaryKeys().size() == entity.getEntities().size()) {
  22. Iterator<String> iterPks = pks.getPrimaryKeys().iterator();
  23. Iterator<IdGenerator> iterEntities = entity.getEntities().iterator();
  24. while (iterPks.hasNext()) {
  25. String id = iterPks.next();
  26. IdGenerator entity = iterEntities.next();
  27. //System.out.println(id + "|" + entity);
  28. entity.parseGenKey(new Object[]{id});
  29. }
  30. }
  31. }
  32. @Override
  33. public Object get(PropertyTokenizer prop) {
  34. // Only the entities or parameters property of BatchInsertEntities
  35. // can be accessed by mapper.
  36. // 这一段是决定最终返回数据结果.
  37. if ("entities".equals(prop.getName()) ||
  38. "parameters".equals(prop.getName())) {
  39. return entity.getEntities();
  40. }
  41. return null;
  42. }
  43. @Override
  44. public String findProperty(String name, boolean useCamelCaseMapping) {
  45. return null;
  46. }
  47. @Override
  48. public String[] getGetterNames() {
  49. return null;
  50. }
  51. @Override
  52. public String[] getSetterNames() {
  53. return null;
  54. }
  55. /**
  56. * 此函数返回类型和BatchInsertEntitiesTypeHandler的泛型类型一致.
  57. * Jdbc3KeyGenerator.
  58. * Class<?> keyPropertyType = metaParam.getSetterType(keyProperties[i]);
  59. * TypeHandler<?> th = typeHandlerRegistry.getTypeHandler(keyPropertyType);
  60. *
  61. * @param name
  62. * @return
  63. * @see org.apache.ibatis.reflection.wrapper.ObjectWrapper#getSetterType(java.lang.String)
  64. */
  65. @Override
  66. public Class<?> getSetterType(String name) {
  67. // Return the primary key setter type.
  68. // Here, we return the BatchInsertEntityPrimaryKeys because
  69. // there are several primary keys in the result set of
  70. // INSERT statement.
  71. return BatchInsertEntityPrimaryKeys.class;
  72. }
  73. @Override
  74. public Class<?> getGetterType(String name) {
  75. return null;
  76. }
  77. @Override
  78. public boolean hasSetter(String name) {
  79. // In BatchInsertObjectWrapper, name is the primary key property name.
  80. // Always return true here without checking if there is such property
  81. // in BatchInsertEntities#getEntities().get(0) . The verification be
  82. // postphone until setting the PK value at this.set method.
  83. return true;
  84. }
  85. @Override
  86. public boolean hasGetter(String name) {
  87. return false;
  88. }
  89. @Override
  90. public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
  91. return null;
  92. }
  93. @Override
  94. public boolean isCollection() {
  95. return false;
  96. }
  97. @Override
  98. public void add(Object element) {
  99. }
  100. @Override
  101. public <E> void addAll(List<E> element) {
  102. }
  103. }

自定义ObjectWrapperFactory

  1. package org.wit.ff.jdbc.id;
  2. import org.apache.ibatis.reflection.MetaObject;
  3. import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
  4. import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
  5. public class BatchInsertObjectWrapperFactory implements ObjectWrapperFactory {
  6. public boolean hasWrapperFor(Object object) {
  7. return null != object && BatchInsertEntities.class.isAssignableFrom(object.getClass());
  8. }
  9. public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
  10. return new BatchInsertObjectWrapper(metaObject, (BatchInsertEntities<IdGenerator>)object);
  11. }
  12. }

源代码分析#

  • 为什么定义一个BatchInsertEntities而不直接使用List
  • 自定义TypeHandler的目的
  • 自定义ObjectWrapper(factory)

此事要回到源码当中找答案。

1)上文的时序图定位主键核心处理代码起始方法:org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator.populateAfter

**注意mapper配置文件配置 useGeneratedKeys="true" keyProperty="id" keyColumn="ID" **


  1. public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
  2. List<Object> parameters = new ArrayList<Object>();
  3. parameters.add(parameter);
  4. processBatch(ms, stmt, parameters);
  5. }
  6. public void processBatch(MappedStatement ms, Statement stmt, List<Object> parameters) {
  7. ResultSet rs = null;
  8. try {
  9. rs = stmt.getGeneratedKeys();
  10. final Configuration configuration = ms.getConfiguration();
  11. final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  12. final String[] keyProperties = ms.getKeyProperties();
  13. final ResultSetMetaData rsmd = rs.getMetaData();
  14. TypeHandler<?>[] typeHandlers = null;
  15. if (keyProperties != null && rsmd.getColumnCount() >= keyProperties.length) {
  16. for (Object parameter : parameters) {
  17. if (!rs.next()) break; // there should be one row for each statement (also one for each parameter)
  18. final MetaObject metaParam = configuration.newMetaObject(parameter);
  19. // 1,找typeHandlers的逻辑为关键.
  20. if (typeHandlers == null) typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties);
  21. // 2, 填充键值.
  22. populateKeys(rs, metaParam, keyProperties, typeHandlers);
  23. }
  24. }
  25. } catch (Exception e) {
  26. throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);
  27. } finally {
  28. if (rs != null) {
  29. try {
  30. rs.close();
  31. } catch (Exception e) {
  32. // ignore
  33. }
  34. }
  35. }
  36. }

步骤1 查找TypeHandler

BatchInsertEntitiesTypeHandler负责处理BatchInsertEntityPrimaryKeys类型,并定义了getNull(rs,int index)方法,后面可以看到这个方法在主键填充时被调用.

  1. private TypeHandler<?>[] getTypeHandlers(TypeHandlerRegistry typeHandlerRegistry, MetaObject metaParam, String[] keyProperties) {
  2. TypeHandler<?>[] typeHandlers = new TypeHandler<?>[keyProperties.length];
  3. for (int i = 0; i < keyProperties.length; i++) {
  4. if (metaParam.hasSetter(keyProperties[i])) {
  5. // metaParam getSetterType --> BatchInsertObjectWrapper定义了getSetterType,参考MetaObject中获取getSetterType的源码,实际是从自定义的ObjectWrapper中获取
  6. Class<?> keyPropertyType = metaParam.getSetterType(keyProperties[i]);
  7. // 从spring xml 配置中找TypeHandler的配置.
  8. TypeHandler<?> th = typeHandlerRegistry.getTypeHandler(keyPropertyType);
  9. typeHandlers[i] = th;
  10. }
  11. }
  12. return typeHandlers;
  13. }

小结:获取TypeHandler的过程实际是依据ObjectWrapper指定的SetterType拿到KeyPropertyType(主键类型),再通过主键类型从用户配置的SessionFactory当中获取(上文SessionFactory中配置BatchInsertEntitiesTypeHandler)

步骤2 填充主键

请参考BaseTypehandler中的getResult方法,实际调用了getNullResult方法,此方法BatchInsertEntitiesTypeHandler已有实现,经过调试发现它依据配置的主键名称"id"从resultset中获取了一列id值。

  1. private void populateKeys(ResultSet rs, MetaObject metaParam, String[] keyProperties, TypeHandler<?>[] typeHandlers) throws SQLException {
  2. for (int i = 0; i < keyProperties.length; i++) {
  3. TypeHandler<?> th = typeHandlers[i];
  4. if (th != null) {
  5. // 即调用BatchInsertEntitiesTypeHandler的public BatchInsertEntityPrimaryKeys getNullableResult(ResultSet rs, int columnIndex)的方法
  6. // 将主键值记录在BatchInsertEntityPrimaryKeys的对象当中,此时value的类型是BatchInsertEntityPrimaryKeys.
  7. Object value = th.getResult(rs, i + 1);
  8. // 调用 org.apache.ibatis.reflection.MetaObject
  9. metaParam.setValue(keyProperties[i], value);
  10. }
  11. }
  12. }
  13. public void setValue(String name, Object value) {
  14. // 被设置的属性是不含有"." , 目前是id, 而不是(item.id)这样的字符串 因此会执行else.
  15. PropertyTokenizer prop = new PropertyTokenizer(name);
  16. if (prop.hasNext()) {
  17. MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
  18. if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
  19. if (value == null && prop.getChildren() != null) {
  20. return; // don't instantiate child path if value is null
  21. } else {
  22. metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
  23. }
  24. }
  25. metaValue.setValue(prop.getChildren(), value);
  26. } else {
  27. // 核心执行逻辑.
  28. // 此处的objectWrapper 对应我们自定义的org.wit.ff.BatchInsertObjectWrapper
  29. objectWrapper.set(prop, value);
  30. }
  31. }

小结:依据目标TypeHandler,调用getNullResult处理主键,从ResultSet当中拿到一列主键值,并包装成BatchInsertEntityPrimaryKeys返回,作为参数执行目标ObjectWrapper的set方法,请参考上文BatchInsertObjectWrapper。

至此,从ResultSet返回的主键值列表已经被我们自定义的ObjectWrapper截获。

步骤3 BatchInsertObjectWrapper填充主键值到原始对象列表

HowTown类型实现了IdGenerator接口, 调用parseGenKey即可填充主键到目标对象上,详情参考上文的HownTown源码。

  1. @Override
  2. public void set(PropertyTokenizer prop, Object value) {
  3. // check the primary key type existed or not when setting PK by reflection.
  4. BatchInsertEntityPrimaryKeys pks = (BatchInsertEntityPrimaryKeys) value;
  5. if (pks.getPrimaryKeys().size() == entity.getEntities().size()) {
  6. Iterator<String> iterPks = pks.getPrimaryKeys().iterator();
  7. Iterator<IdGenerator> iterEntities = entity.getEntities().iterator();
  8. while (iterPks.hasNext()) {
  9. String id = iterPks.next();
  10. IdGenerator entity = iterEntities.next();
  11. //System.out.println(id + "|" + entity);
  12. entity.parseGenKey(new Object[]{id});
  13. }
  14. }
  15. }

三个问题的答案##

1)自定义存储对象列表的结构的原因在于MyBatis处理主键时始终将对象作为"一个"来看待,并且要绑定主键类型,而List是集合类型,类型是List

2)由于1)中自定义了存储结构(BatchInsertEntities)需要处理主键,因此需要定义一个新的主键类型BatchInsertEntityPrimaryKeys 并绑定一个TypeHandler才可以处理此类型

3) ObjectWrapper和TypeHandler实际上是相辅相成的关系,有了类型处理器将ResultSet中的主键数据转换为目标对象可接受的类型,那么填充目标对象主键的工作就由ObjectWrapper来完成了,它们是相互协作的关系。

总结#

如果仍然对上述过程有疑问,请务必调试代码,作者不太聪明,调试了n次才读懂了完整过程。

最后给个提示,一个普通Bean是如何填充主键的,请查看org.apache.ibatis.reflection.wrapper.BeanWrapper及org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator,想必必有收获。

QA#

太累了,这一篇。。。

轻量级封装DbUtils&Mybatis之四MyBatis主键的更多相关文章

  1. 轻量级封装DbUtils&Mybatis之三MyBatis分页

    MyBatis假分页 参考DefaultResultSetHandler的skipRows方法. 温馨提示:部分代码请参考轻量级封装DbUtils&Mybatis之一概要 解决方案 1)之前公 ...

  2. MyBatis insert返回主键(sqlserver2008)

    mybatis insert返回主键(sqlserver2008)   MyBatisXML配置,下面两种方式都行 方式1: <insert id="insert" para ...

  3. MyBatis自动获取主键,MyBatis使用Oracle返回主键,Oracle获取主键

    MyBatis自动获取主键,MyBatis使用Oracle返回主键,Oracle获取主键 >>>>>>>>>>>>>> ...

  4. 【转】mybatis 自增主键配置

    mybatis自增主键配置(?) mybatis进行插入操作时,如果表的主键是自增的,针对不同的数据库相应的操作也不同.基本上经常会遇到的就是Oracle Sequece 和 MySQL 自增主键,至 ...

  5. 轻量级封装DbUtils&Mybatis之一概要

    Why 一时兴起,自以为是的对Jdbc访问框架做了一个简单的摸底,近期主要采用Mybatis,之前也有不少采用Dbutils,因此希望能让这两个框架折腾的更好用. DbUtils:非常简单的Jdbc访 ...

  6. 轻量级封装DbUtils&Mybatis之二Dbutils

    DbUtils入门 Apache出品的极为轻量级的Jdbc访问框架,核心类只有两个:QueryRunner和ResultSetHandler. 各类ResultSetHandler: ArrayHan ...

  7. mybatis insert 返回主键

    分享牛,分享牛原创.ssm整合的时候,我们操作mybatis insert 的时候,需要返回插入的主键,因为主键是自增的,这个时候怎么办呢?很简单看一下下面的代码示例: 1.1.1. 代码定义 pub ...

  8. jdbc、Mybatis插入数据主键回显的实现方法

    插入数据的时候,往往需要获取主键值.但是有时候主键是自增长的那么,就不太适用手动添加主键值了,此时需要一种可以回显主键参数的方法, 下面以jdbc.mybatis的实现举例 此时使用的是jdbc的话或 ...

  9. mybatis insertUseGeneratedKeys 返回主键为null

    package tk.mybatis.mapper.common.special; import org.apache.ibatis.annotations.InsertProvider; impor ...

随机推荐

  1. Java Web中Kaptcha实现验证码

    首先进行导入相应的jar包: 1.如果是maven项目,在你的pom文件中进行添加如下代码,将自动下载jar包到你的工程中: <dependency>            <gro ...

  2. Spring入门5.事务管理机制

    Spring入门5.事务管理机制 20131126 代码下载 : 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 回顾之前的知识,Spring 最为核心的两个部分 ...

  3. Hrbust 1535 相爱

    Description 静竹在斐波那契的帮助下,渐渐的和数学相爱了.和数学在一起最有意思的就是它能够出一些特别有意思并且巧妙的题目让静竹来思考.这次也不例外,给静竹两个数a,b,又给出了加,减,乘,除 ...

  4. jmeter的三种参数化

    以FTP请求(用户.密码)为例:(其他都相同) 1.文件参数化 使用配置元件中的CSV Data Set Config 配置CSV Data Set Config: 文件中存储ftp登录的用户名和密码 ...

  5. c# 多线程调用窗体上的控件 示例

    private delegate void InvokeCallback(string msg); private void SetCountValue(string s) { if (this.fo ...

  6. c++下使用邮槽实现进程间通信

    Windows API提供了邮槽和命名管道两种机制来实现进程间通信,在这里使用C++实现邮槽. 邮槽是Windows提供的一种进程间单向通信的机制,进程中的一方只能读取(或写入)数据,而另一方只能写入 ...

  7. [AirFlow]AirFlow使用指南二 DAG定义文件

    1. Example """ Code that goes along with the Airflow tutorial located at: https://git ...

  8. 基于Ubuntu16.04的GeForce GTX 1080驱动安装,遇到的问题及对应的解决方法

    1.在主机上插上GPU之后,查看设备: $ nvidia-smi Tue Dec :: +------------------------------------------------------- ...

  9. jQuery插件实现表格隔行换色且感应鼠标高亮行变色

    实现表格隔行换色,且感应鼠标行变色的方法有很多,在本文将为大家介绍的是使用jQuery插件来实现,具体如下 看代码: : css代码:  

  10. crm 03--> crm与权限结合

    ---恢复内容开始--- 1:先分组 2:给权限分组 3:具体的权限(即设计url) 二:制作左侧菜单,显示当前用户拥有的权限 关于项目下的templates里的HTML查找顺序 先从根目录找,找不到 ...