和select相比皿伺,insert要簡(jiǎn)單的多员辩。只有讓他返回主鍵時(shí),由于不同數(shù)據(jù)庫(kù)的主鍵生成方式不同鸵鸥,這種情況下會(huì)有一些復(fù)雜奠滑。
MyBatis參考文檔:
中文版:http://www.mybatis.org/mybatis-3/zh/index.html
英文版:http://www.mybatis.org/mybatis-3/
工具
JDK 1.6及以上版本
MyBatis 3.30版本
MySQL 6.3版本
Eclipse4 及以上版本
Apache Maven 構(gòu)建工具
項(xiàng)目源碼下載地址:https://github.com/JFAlex/MyBatis/tree/master/MyBatis_No.3/alex
簡(jiǎn)單的insert用法
現(xiàn)在我們向用戶表中添加一個(gè)新用戶丹皱,在UserMapper接口中添加如下方法:
public int insert(SysUser sysUser);
在UserMapper.xml中添加如下代碼:
<insert id="insert">
INSERT INTO sys_user (id,user_name, user_password,
user_email, user_info, head_img, create_time)
VALUES
(#{id},#{userName},#{userPassword},#{userEmail},#{userInfo},#{headImg
, jdbcType=BLOB},#{createTime,jdbcType=TIMESTAMP})
</insert>
首先解釋一下<select>標(biāo)簽下包含的屬性。
- id:命名空間的唯一標(biāo)識(shí)宋税,可以用來(lái)代表這條語(yǔ)句摊崭。
- parameterType:即將傳入的語(yǔ)句參數(shù)的完全限定類名或別名。這個(gè)屬性是可選的杰赛,因?yàn)镸yBatis可以推斷出傳入語(yǔ)句的具體參數(shù)呢簸,因此不建議配置該屬性。
- flushCache:默認(rèn)值為true乏屯,任何時(shí)候只要語(yǔ)句被調(diào)用根时,都會(huì)清空一級(jí)緩存和二級(jí)緩存。
- timeout:設(shè)置在拋出異常之前辰晕,驅(qū)動(dòng)程序等待數(shù)據(jù)庫(kù)返回請(qǐng)求結(jié)果的秒數(shù)蛤迎。
- statementType:對(duì)于STATEMENT、CALLABLE含友、PREPARED忘苛,MyBatis會(huì)分別使用對(duì)應(yīng)的Statement、CallableStatement唱较、PreparedStatement,默認(rèn)值為PREPARED召川。
- useGeneratedKeys:默認(rèn)值為false南缓。如果設(shè)置為true,MyBati會(huì)使用JDBC的getGeneratedKeys方法來(lái)取出有數(shù)據(jù)庫(kù)內(nèi)部生成的主鍵荧呐。
- keyProperty:用于設(shè)置MyBatis通過getGeneratedKeys獲取主鍵值后汉形,將要賦值給哪個(gè)屬性(屬性名)后雷。
- keyColumn:僅對(duì)INSERT和UPDATE有用苞慢。通過生成的鍵值設(shè)置表中的列名托修,這個(gè)設(shè)置僅在某些數(shù)據(jù)庫(kù)(如PostgreSQL)中是必須的偶翅,當(dāng)主鍵列不是表中的第一列時(shí)需要設(shè)置置森。如果希望得到多個(gè)生成的列愕难,也可以是逗號(hào)分隔的屬性名稱列表丰捷。
- databaseId:如果配置了databaseIdProvider篡石,MyBatis會(huì)加載所有的不帶databaseId的或匹配當(dāng)前databaseId的語(yǔ)句概耻。如果同時(shí)存在帶databaseId和不帶databaseId的語(yǔ)句使套,則不帶databaseId的語(yǔ)句被忽略。
在XML配置SQL語(yǔ)句時(shí)我們可以看到鞠柄,最后兩個(gè)字段(#{headImg和 jdbcType=BLOB},#{createTime,jdbcType=TIMESTAMP})中添加了jdbcType侦高,這里的作用是為了防止類型錯(cuò)誤,對(duì)于一些特殊的數(shù)據(jù)類型厌杜,需要指定具體的jdbcType值奉呛。
由于數(shù)據(jù)庫(kù)區(qū)分date、time、datetime類型瞧壮,但是Java中一般都使用java.util.Date類型登馒,因此為了保證數(shù)據(jù)庫(kù)類型的正確,需要手動(dòng)指定日期類型馁痴,date谊娇、time、datetime對(duì)應(yīng)的JDBC類型分別為DATE罗晕、TIME济欢、TIMESTAMP。
現(xiàn)在在UserMapperTest測(cè)試類中添加一個(gè)方法來(lái)測(cè)試insert方法:
@Test
public void testInsert(){
// 獲取SqlSession
SqlSession sqlSession = getSqlSession();
// 獲取UserMapper接口
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//創(chuàng)建一個(gè)user對(duì)象
SysUser user = new SysUser();
user.setUserName("insert");
user.setUserPassword("123456");
user.setUserEmail("mybatis@my.test");
user.setUserInfo("insert data");
//正常情況下應(yīng)該存入一張圖片
user.setHeadImg(new byte[]{1,2,3});
user.setCreateTime(new Date());
//將新建的對(duì)象插入數(shù)據(jù)庫(kù)中小渊,特別注意這里的返回值result是執(zhí)行的SQL影響的行數(shù)
int result = userMapper.insert(user);
System.out.println("插入成功數(shù)據(jù)條數(shù)為:" + result);
}catch(Exception e){
e.printStackTrace();
} finally {
//為了不對(duì)其他的測(cè)試造成影響法褥,此處進(jìn)行數(shù)據(jù)回滾
//由于默認(rèn)的sqlSessionFactory.openSession()是不會(huì)自動(dòng)提交的,因此如果不手動(dòng)進(jìn)行commit操作酬屉,數(shù)據(jù)也不會(huì)寫入數(shù)據(jù)庫(kù)
sqlSession.rollback();
// 關(guān)閉SqlSession
sqlSession.close();
}
}
右鍵單擊測(cè)試類半等,在Run As選項(xiàng)中選擇JUnit Test執(zhí)行測(cè)試。測(cè)試通過呐萨,控制臺(tái)將會(huì)打印如下日志:
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 640363654.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@262b2c86]
DEBUG [main] - ==> Preparing: INSERT INTO sys_user (id,user_name, user_password, user_email, user_info, head_img, create_time) VALUES (?,?,?,?,?,?,?)
DEBUG [main] - ==> Parameters: null, insert(String), 123456(String), mybatis@my.test(String), insert data(String), java.io.ByteArrayInputStream@462d5aee(ByteArrayInputStream), 2017-08-15 10:47:26.377(Timestamp)
DEBUG [main] - <== Updates: 1
插入成功數(shù)據(jù)條數(shù)為:1
DEBUG [main] - Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@262b2c86]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@262b2c86]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@262b2c86]
DEBUG [main] - Returned connection 640363654 to pool.
通過上面的輸出日志杀饵,我們可以看到
2017-08-15 10:47:26.377(Timestamp)
如果我們將XML中的INSERT的SQL語(yǔ)句修改如下
#{createTime,jdbcType=DATE}
再次執(zhí)行測(cè)試,我們可以看到日志輸出的日期字段的值會(huì)變成
2017-08-15(Date)
這個(gè)值就是我們?cè)O(shè)置的DATE谬擦,但是如果我們將jdbcType的值設(shè)置為TIME切距,再次進(jìn)行測(cè)試,我們測(cè)試將會(huì)失敗
### Error updating database. Cause: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Incorrect datetime value: '10:56:17' for column 'create_time' at row 1
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: INSERT INTO sys_user (id,user_name, user_password, user_email, user_info, head_img, create_time) VALUES (?,?,?,?,?,?,?)
### Cause: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Incorrect datetime value: '10:56:17' for column 'create_time' at row 1
通過錯(cuò)誤信息惨远,我們可以知曉錯(cuò)誤原因是數(shù)據(jù)庫(kù)中的字段類型為datetime,但是這里的這里只有time部分的值谜悟。
數(shù)據(jù)庫(kù)的datetime類型可以存儲(chǔ)DATE(時(shí)間部分默認(rèn)為00:00:00)和TIMESTAMP這兩種類型的事件,不能存儲(chǔ)TIME類型的時(shí)間北秽。
通過上面的測(cè)試葡幸,我們應(yīng)該可以理解jdbcType值得作用了。
返回主鍵值
之前我們進(jìn)行INSERT操作時(shí)贺氓,返回的數(shù)據(jù)為影響的數(shù)據(jù)行數(shù)蔚叨,但是很多時(shí)候我們并不需要這個(gè)返回?cái)?shù)據(jù),而是需要知道這條數(shù)據(jù)的主鍵辙培,那么我們可以怎么獲得這個(gè)主鍵的值呢缅叠?
使用JDBC方式返回主鍵自增的值
在使用主鍵自增(如MySQL、SQL Server數(shù)據(jù)庫(kù))時(shí)虏冻,插入數(shù)據(jù)庫(kù)后科技能需要得到自增的主鍵值肤粱,然后使用這個(gè)值進(jìn)行一些其他的操作。
現(xiàn)在增加一個(gè)insert2方法厨相,首先在userMapper接口中增加insert2方法
public int insert2(SysUser sysUser);
然后在XML中新增一個(gè)insert2方法
<insert id="insert2" useGeneratedKeys="true" keyProperty="id">
INSERT INTO sys_user (user_name, user_password,
user_email, user_info, head_img, create_time)
VALUES
(#{userName},#{userPassword},#{userEmail},#{userInfo},#{headImg
, jdbcType=BLOB},#{createTime,jdbcType=TIMESTAMP})
</insert>
比較insert和insert2领曼,其中最主要的變化就是insert2比insert多添加了兩個(gè)屬性
useGeneratedKeys="true"
keyProperty="id"
useGeneratedKeys設(shè)置為true后鸥鹉,MyBatis會(huì)使用JDBC的getGeneratedKeys方法來(lái)取出由數(shù)據(jù)庫(kù)內(nèi)部生成的主鍵。獲得主鍵后將其賦值給keyProperty配置的id屬性庶骄。當(dāng)需要配置多個(gè)屬性時(shí)毁渗,使用逗號(hào)隔開,這種情況下通常還需要設(shè)置keyColumn屬性单刁,按順序指定數(shù)據(jù)庫(kù)的列灸异,這里的值會(huì)和keyProperty配置的屬性一一對(duì)應(yīng)。
由于要使用數(shù)據(jù)庫(kù)返回的主鍵羔飞,所有SQL中去掉了id列和#{id}屬性肺樟。
然后寫一個(gè)測(cè)試驗(yàn)證是否返回了SysUser的主鍵值,在UserMapperTest測(cè)試類中添加代碼:
@Test
public void testInsert2(){
// 獲取SqlSession
SqlSession sqlSession = getSqlSession();
// 獲取UserMapper接口
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//創(chuàng)建一個(gè)user對(duì)象
SysUser user = new SysUser();
user.setUserName("insert");
user.setUserPassword("123456");
user.setUserEmail("mybatis@my.test");
user.setUserInfo("insert data");
//正常情況下應(yīng)該存入一張圖片
user.setHeadImg(new byte[]{1,2,3});
user.setCreateTime(new Date());
//將新建的對(duì)象插入數(shù)據(jù)庫(kù)中逻淌,特別注意這里的返回值result是執(zhí)行的SQL影響的行數(shù)
int result = userMapper.insert2(user);
System.out.println("插入成功數(shù)據(jù)條數(shù)為:" + result);
System.out.println("主鍵id為:"+user.getId());
}catch(Exception e){
e.printStackTrace();
} finally {
//為了不對(duì)其他的測(cè)試造成影響么伯,此處進(jìn)行數(shù)據(jù)回滾
//由于默認(rèn)的sqlSessionFactory.openSession()是不會(huì)自動(dòng)提交的,因此如果不手動(dòng)進(jìn)行commit操作卡儒,數(shù)據(jù)也不會(huì)寫入數(shù)據(jù)庫(kù)
sqlSession.rollback();
// 關(guān)閉SqlSession
sqlSession.close();
}
}
右鍵單擊測(cè)試類田柔,在Run As選項(xiàng)中選擇JUnit Test執(zhí)行測(cè)試。測(cè)試通過骨望,控制臺(tái)將會(huì)打印如下日志:
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 99451533.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5ed828d]
DEBUG [main] - ==> Preparing: INSERT INTO sys_user (user_name, user_password, user_email, user_info, head_img, create_time) VALUES (?,?,?,?,?,?)
DEBUG [main] - ==> Parameters: insert(String), 123456(String), mybatis@my.test(String), insert data(String), java.io.ByteArrayInputStream@462d5aee(ByteArrayInputStream), 2017-08-15 11:50:33.443(Timestamp)
DEBUG [main] - <== Updates: 1
插入成功數(shù)據(jù)條數(shù)為:1
主鍵id為:7
DEBUG [main] - Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@5ed828d]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5ed828d]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5ed828d]
DEBUG [main] - Returned connection 99451533 to pool.
如果想知道這里打印的id的值是否正確硬爆,我們可以將測(cè)試方法中的rollback()方法修改為commit()方法,將結(jié)果提交到數(shù)據(jù)庫(kù)(否則數(shù)據(jù)只存在session中擎鸠,會(huì)隨著程序的關(guān)閉而消失)摆屯,然后查看數(shù)據(jù)庫(kù)驗(yàn)證id是否一致。
使用selectKey返回主鍵的值
上面這種獲取主鍵的方法只適用于支持主鍵自動(dòng)增長(zhǎng)的數(shù)據(jù)庫(kù)糠亩。但是有些數(shù)據(jù)庫(kù)(如Oracle)不提供主鍵自增的功能,而是使用序列得到一個(gè)值准验,然后講這個(gè)值賦值給id赎线,在將數(shù)據(jù)插入到數(shù)據(jù)庫(kù)中,對(duì)于這種情況糊饱,我們就可以使用另一種方法:使用<selectKey>標(biāo)簽來(lái)獲取主鍵的值垂寥,這種方式不僅是用于不提供主鍵自增功能的數(shù)據(jù)庫(kù),而且也適用于提供主鍵自增功能的數(shù)據(jù)庫(kù)另锋。
我使用的是MySQL數(shù)據(jù)庫(kù)(提供主鍵自增)滞项,當(dāng)然如果你使用的是Oracel數(shù)據(jù)庫(kù)(不提供主鍵自增),也是沒有問題的夭坪。
在接口和XML中新增一個(gè)方法insert3文判,UserMapper接口中的方法如下:
public int insert3(SysUser sysUser);
然后添加UserMapper.xml中的代碼:
<insert id="insert3">
INSERT INTO sys_user (id,user_name, user_password,
user_email, user_info, head_img, create_time)
VALUES
(#{id},#{userName},#{userPassword},#{userEmail},#{userInfo},#{headImg
, jdbcType=BLOB},#{createTime,jdbcType=TIMESTAMP})
<selectKey keyColumn="id" resultType="long" keyProperty="id" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
我們可以很明顯的看出,這里的XML配置中除了我們常見的SQL語(yǔ)句外室梅,還包含了一個(gè)selectKey標(biāo)簽戏仓。
selectKey標(biāo)簽的keyColumn疚宇、keyProperty和上一種方法中的useGengeratedKeys的用法含義相同,這里的resultType用于設(shè)置返回值類型赏殃。order屬性的設(shè)置和使用的數(shù)據(jù)庫(kù)有關(guān)敷待。在MySQL數(shù)據(jù)庫(kù)中,order屬性設(shè)置的值是AFTER仁热,因?yàn)楫?dāng)前記錄的主鍵是在insert語(yǔ)句執(zhí)行成功后才獲取到的举哟。而在Oracel數(shù)據(jù)庫(kù)中物蝙,order屬性的值要設(shè)置為BEFORE诬乞,這是因?yàn)镺racle數(shù)據(jù)庫(kù)中需要先從序列獲取值森瘪,然后將值作為主鍵插入到數(shù)據(jù)庫(kù)中扼睬。
selectKey標(biāo)簽放置的位置可以是在INSERT語(yǔ)句之后,也可以放置在INSERT語(yǔ)句之前,這并不影響執(zhí)行順序粪躬,而影響執(zhí)行順序的主要因素是order屬性的值
注意:
Oracle方式的INSERT語(yǔ)句中需要明確的列出id列和值#{id},因?yàn)閳?zhí)行selectKey中的語(yǔ)句后id就有值了泳唠,我們需要把這個(gè)序列作為主鍵插入到數(shù)據(jù)庫(kù)中孙援,所以必須制定id列,如果不指定這一列础淤,數(shù)據(jù)庫(kù)就會(huì)因?yàn)橹麈I不能為空而拋出異常。所以我們使用selectKey方式獲取主鍵id時(shí),不管使用的是什么數(shù)據(jù)庫(kù)凑兰,我們都可以添加上主鍵列和值(這里為id列和值#{id})
右鍵單擊測(cè)試類茅坛,在Run As選項(xiàng)中選擇JUnit Test執(zhí)行測(cè)試曹鸠。測(cè)試通過,控制臺(tái)將會(huì)打印如下日志:
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 2050835901.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7a3d45bd]
DEBUG [main] - ==> Preparing: INSERT INTO sys_user (id,user_name, user_password, user_email, user_info, head_img, create_time) VALUES (?,?,?,?,?,?,?)
DEBUG [main] - ==> Parameters: null, insert(String), 123456(String), mybatis@my.test(String), insert data(String), java.io.ByteArrayInputStream@757942a1(ByteArrayInputStream), 2017-08-15 13:42:11.322(Timestamp)
DEBUG [main] - <== Updates: 1
DEBUG [main] - ==> Preparing: SELECT LAST_INSERT_ID()
DEBUG [main] - ==> Parameters:
TRACE [main] - <== Columns: LAST_INSERT_ID()
TRACE [main] - <== Row: 9
DEBUG [main] - <== Total: 1
插入成功數(shù)據(jù)條數(shù)為:1
主鍵id為:9
DEBUG [main] - Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@7a3d45bd]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7a3d45bd]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7a3d45bd]
DEBUG [main] - Returned connection 2050835901 to pool.
我們還需要注意一個(gè)問題,就是selectKey元素中的內(nèi)容,它的內(nèi)容是一個(gè)獨(dú)立的SQL語(yǔ)句腥椒,MySQL中的語(yǔ)句是SELECT LAST_INSERT_ID()用于獲取數(shù)據(jù)庫(kù)中最后插入數(shù)據(jù)的ID值。
以下是一些數(shù)據(jù)庫(kù)配置selectKey中回寫主鍵的SQL:
- ORACLE 使用 SELECT SEQ_ID.nextval from dual (SEQ_ID為主鍵對(duì)應(yīng)的序列名稱)
- DB2 使用 VALUES IDENTITY_VAL_LOCAL()
- MYSQL 使用 SELECT LAST_INSERT_ID()
- SQLSERVER 使用 SELECT SCOPE_IDENTITY()
- DERBY 使用 VALUES IDENTITY_VAL_LOCAL()
- HSQLDB 使用 CALL IDENTITY()
- SYBASE 使用 SELECT @@IDENTITY()
- DB2_MF 使用 SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1
- CLOUDSCAPE 使用 VALUES IDENTITY_VAL_LOCAL()
以上所列數(shù)據(jù)庫(kù)除Oracle數(shù)據(jù)為不支持主鍵自增惋戏,其余數(shù)據(jù)庫(kù)均支持主鍵自增,所以只有在使用Oracle是order屬性配置為BEFORE些膨,其余都配置order屬性為AFTER。
項(xiàng)目源碼下載地址:https://github.com/JFAlex/MyBatis/tree/master/MyBatis_No.3/alex
上一篇: 【MyBatis】 MyBatis修煉之四 MyBatis XML方式的基本用法(SELECT)
下一篇: 【MyBatis】 MyBatis修煉之六 MyBatis XML方式的基本用法(UPDATE职抡、DELETE)