目录
文概览:为了介绍Mybatis获取自增主键,分别介绍了通过Mysql和JDbc两种方式。
1 Mysql实现
1.1 方式1 max(id)
最简单的方法是通过“select max(id) from tablename”的做法,但是显然这种做法需要考虑并发的情况,需要在事务中对主表加以“X锁“,待获得max(id)的值以后,再解锁。
1 |
select max(id) from tablename |
1.2 方式2 last_insert_id()
LAST_INSERT_ID是基于Connection的,只要每个线程都使用独立的 Connection对象,LAST_INSERT_ID函数将返回该Connection对AUTO_INCREMENT列最新的insert or update 操作生成的第一个record的ID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Statement stmt = null; ResultSet rs = null; try { stmt = conn.createStatement(); // ... // 省略建表 // ... stmt.executeUpdate( "INSERT INTO autoIncTutorial (dataField) " + "values ('Can I Get the Auto Increment Field?')"); int autoIncKeyFromFunc = -1; rs = stmt.executeQuery("SELECT LAST_INSERT_ID()"); if (rs.next()) { autoIncKeyFromFunc = rs.getInt(1); } else { // throw an exception from here } System.out.println("Key returned from " + "'SELECT LAST_INSERT_ID()': " + autoIncKeyFromFunc); } finally {...} |
1.3 方式3 select @@IDENTITY
这个方式和LAST_INSERT_ID()效果是一样的,所以二者选一个就可以
2 JDBC方式
2.1 getGeneratedKeys()
1. 介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/** * Retrieves any auto-generated keys created as a result of executing this * <code>Statement</code> object. If this <code>Statement</code> object did * not generate any keys, an empty <code>ResultSet</code> * object is returned. * *<p><B>Note:</B>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 <code>ResultSet</code> object containing the auto-generated key(s) * generated by the execution of this <code>Statement</code> object * @exception SQLException if a database access error occurs or * this method is called on a closed <code>Statement</code> * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method * @since 1.4 */ ResultSet getGeneratedKeys() throws SQLException; |
使用举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
Statement stmt = null; ResultSet rs = null; try { stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_UPDATABLE); // ... // 省略若干行(如上例般创建demo表) // ... stmt.executeUpdate( "INSERT INTO autoIncTutorial (dataField) " + "values ('Can I Get the Auto Increment Field?')", Statement.RETURN_GENERATED_KEYS); // 向驱动指明需要自动获取generatedKeys! int autoIncKeyFromApi = -1; rs = stmt.getGeneratedKeys(); // 获取自增主键! if (rs.next()) { autoIncKeyFromApi = rs.getInt(1); } else { // throw an exception from here } rs.close(); rs = null; System.out.println("Key returned from getGeneratedKeys():" + autoIncKeyFromApi); } finally { ... } |
1. JDBC的java.sql.Statement#getGeneratedKeys()的方法与last_insert_id()方法区别:
At times, it can be tricky to use the SELECT LAST_INSERT_ID() query, as that function’s value is scoped to a connection. So, if some other query happens on the same connection, the value is overwritten.
On the other hand, the getGeneratedKeys() method is scoped by the Statement instance, so it can be used even if other queries happen on the same connection, but not on the same Statement instance.
这段话其实就是:对于一个连接Connection,产生两个Statement:statement1和statement2,这两个Statement执行分表别执行插入操作,那么"select last_insert_id"的值是这个连接对应的最新插入的id,但是statement1.getGeneratedKeys()和statement2.getGeneratedKeys值是分别是对应的statement插入时对应的值.
参考:http://dev.mysql.com/doc/connector-j/en/connector-j-usagenotes-last-insert-id.html
3 Mybatis方式
3.1 方式1 getGeneratedKeys属性
1、getGeneratedKeys是<insert>和<update>节点的属性,如<insert id=”” parameterType=”” useGeneratedKeys=”true”>方法。这个属性和JDBC的java.sql.Statement的getGeneratedKeys()作用是一样的。其他属性还有keyProperty和keyColumn两种属性:
- useGeneratedKeys
- keyProperty 指定获取的结果值,赋值给参数对象中相应的属性成员。
- keyColumn 显示的指定数据表的那个列是自增列,这个一般情况下不设置
2、参数类型是对象类型
在Dao中insert实现时,parameterType为对象类型时,如下
1 |
int insert(QsightSyncTreeLogEntity qsightSyncTreeLogEntity); |
mybatis对于插入操作设置插入后id,QsightSyncTreeLogEntity中id就变为插入后的id。对应的mapper.xml为:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<insert id="insert" parameterType="com.qunar.hotel.crm.model.entertainment.QsightSyncTreeLogEntity" useGeneratedKeys="true" keyProperty="id"> insert into qsight_sync_info_log ( qsight_id, entertainment_id, operate_time ) values ( #{qsightId}, #{entertainmentId}, now() ) </insert> |
3. 参数不是对象类型
这种情况下获取增量主键的方法是:将参数设置成map类型
(1)举例,如在Dao中insert实现时,如下
1 |
int insert(@Param("version") String version); |
对应的mapper.xml文件为:
1 2 3 4 5 6 7 8 9 10 |
<insert id="insert" parameterType="java.lang.String" useGeneratedKeys="true" keyProperty="id"> insert into hour_room_task_log( version, create_time ) values( #{version}, now() ) </insert> |
此时如果要获取自增id,dao就需要改写为
1 |
int inset(Map map) |
此时就可以通过map.get(“id”)就就可以读取自增id了
(2)参数不能设置为基本类型的原因,如下
这里不能通过如下方法来做
1 2 |
int insert(@Param("version") String version,@Param("id")String id); int insert(@Param("version") String version,@Param("id")int id); |
这是引入举例如这样一个函数
1 2 3 4 |
void getIncrement (int num,String str){ num =10; str = "hello" } |
我们执行如下操作
1 2 3 4 |
int num=1; String str= "kk"; getIncrement(num,str); System.out.prinft("num="num",str="+str); |
此时执行记过仍然为
num=1,str=kk
所以参数设置为基本类型不能把keyProperty的值传递出来。
3.2 方式2 selectkey
1、slectKey的两个作用:
- 作用1:mybatis通过<selectkey>方法来实现mysql的”select select last_insert_id()”和”select @@IDENTITY”对应.
- 作用2:对于不支持自动生成类型的数据库或可能不支持自动生成主键 JDBC 驱动来说,MyBatis 使用这种方法来生成主键。
2、说明keyProperty、keyColumn、order by 、resultType属性
- keyProperty 指定获取的结果值,赋值给参数对象中相应的属性成员。
- keyColumn 显示的指定数据表的那个列是自增列,这个一般情况下不设置
- order by 如果设置为 BEFORE,那么它会首先选择主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 元素 – 这和Oracle 的数据库相似,在插入语句内部可能有嵌入索引调用。所以为了获取插入新数据之后的id,那么这个属性应该设置为AFTER。
- resultType
2、使用<selectKey>来实现mysql的select @@IDENTITY
1 2 3 4 5 6 |
<insert id="saveAptitudeHistory" parameterType="com.qunar.hotel.crm.model.customer.CustomerAptitudeHistoryEntity"> INSERT INTO crm_aptitude_history (..............) <selectKey resultType="int" keyProperty="id"> select @@IDENTITY </selectKey> </insert> |
3、使用<selectKey>来实现mysql的select last_insert_id()
1 2 3 4 5 6 7 |
<insert id="buildManagerAssistantRelation"> <selectKey resultType="int" keyProperty="id" order="AFTER"> SELECT LAST_INSERT_ID() </selectKey> insert into crm_contract_manager_assistant (contract_manager_user_id,assistant_user_id) values ...... </insert> |
3.3 两种方式的总结
1、相同点
无论useGeneratedKeys还是<selectKey>获取自增主键之后,都是通过keyProperty输出.所以需要在dao定义的insert接口函数中有一个参数来接受这个值
- 当参数是一个对象类型时 直接将keyProperty的标注的字段的值设置为自增主键的值
- 当参数不是对象类型时 将参数类型修改为map类型。
1int save(Map map)
2、不同点
优先使用useGeneratedKeys.当对于不支持自动生成类型的数据库或可能不支持自动生成主键 JDBC 驱动,MyBatis才引入<selectKey> 这种方法来生成主键.对于mysql而言,这个数据库具有主键自增的功能,所以直接使用useGeneratedKeys就可以了.而且<selectKey>会执行效率就没有 getGeneratedKeys 高。所以,在”数据库不支持自动生成主键时”的场景中才会想到使用<selectKey>。
(全文完)