Mybatis中的@SelectKey註解

1、建立Maven項目

在pom.xml中,添加mybatis依賴,mysql-jdbc依賴,把編譯版本改成1.8
你問,爲啥mybatis不會自動依賴mysql-jdbc,須要手動寫明?答:由於mysql驅動是經過字符串動態加載的,這是一種「動態依賴」,Maven只能推導出「靜態依賴」。「動態依賴」是一種更加靈活的依賴。html

Maven默認的Java版本是1.6,沒法使用lambda表達式(1.8)和鑽石運算符(1.7)。java

代碼片斷:pom.xmlmysql

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>wyf</groupId>
    <artifactId>xqweb</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.4</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.6</version>
        </dependency>
        
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>utf8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>


</project>

建立好了pom.xml,就能夠開始編碼了。最終的目錄結構以下,下面讓咱們來一步一步建立文件。
web

2、配置Mybatis

代碼片斷:mybatis.xmlspring

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="config.properties">
    </properties>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper class="haha.UserDao"/>
        <mapper resource="user.xml"/>
    </mappers>
</configuration>

代碼片斷:config.propertiessql

username=root
password=haha
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai

把配置信息跟mybatis.xml分開的好處是:更清晰。mybatis屬於代碼區,config.properties改起來比較簡單。數據庫

3、建立實體類User

User有三個屬性:name,age和id,重寫toString()方法便於調試。apache

package haha;
public class User {
String name;
Integer age;
Integer id;
public User(){}
public User(String name,int age){
   this.name=name;
   this.age=age;
}
public String getName() {
   return name;
}

public void setName(String name) {
   this.name = name;
}

public Integer getAge() {
   return age;
}

public void setAge(Integer age) {
   this.age = age;
}

public Integer getId() {
   return id;
}

public void setId(Integer id) {
   this.id = id;
}

@Override
public String toString() {
   return String.format("(id:%d,name:%s,age:%d)", id, name, age);
}
}

相應的,在數據庫中創建一個表userapi

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=119 DEFAULT CHARSET=utf8mb4

4、實現UserDao接口

UserDao接口有兩個功能:插入、查詢所有。數組

package haha;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectKey;

import java.util.List;

public interface UserDao {
@Insert("insert into user(name,age) value(#{name},#{age})")
int insert_withoutPrimaryKey(@Param("name") String name, @Param("age") int age);

int insert_useGeneratedKey(@Param("user") User user);

int insert_selectKey(@Param("user") User user);

@Insert("insert into user(name,age) value(#{user.name},#{user.age})")
@SelectKey(statement = "select last_insert_id()", keyProperty = "user.id", before = false, resultType = int.class)
int insert_selectKeyAnotation(@Param("user") User user);

@Select("select*from user")
List<User> getAll();
}

Mybatis寫SQL語句有兩種方式:一、使用註解;二、使用xml
對於比較長的SQL語句放在xml中,對於比較短的SQL語句放在註解中

在上面定義的UserDao中,insert_userGeneratedKey()和insert_selectKey()兩個函數沒有給出對應的SQL語句,須要在xml文件中進行定義。

代碼片斷:user.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="haha.UserDao">
    <insert id="insert_useGeneratedKey" parameterType="haha.User"
            useGeneratedKeys="true" keyProperty="user.id">
        insert into user set id=#{user.id},name=#{user.name},age=#{user.age}
    </insert>
    <insert id="insert_selectKey" parameterType="haha.User">
        <selectKey keyProperty="user.id" keyColumn="id" order="AFTER" resultType="int">
            SELECT last_insert_id()
        </selectKey>
        insert into user(name,age) VALUE (#{user.name},#{user.age})
    </insert>
</mapper>

5、萬事俱備,只欠東風

編寫一個UserService類測試一下

package haha;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class UserService {

public static void main(String[] args) throws IOException {
   String resource = "mybatis.xml";
   InputStream inputStream = Resources.getResourceAsStream(resource);
   SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
   SqlSessionFactory factory = builder.build(inputStream);
   SqlSession session = factory.openSession();
   UserDao dao = session.getMapper(UserDao.class);
   //使用默認主鍵
   int affectedRows = dao.insert_withoutPrimaryKey("張三", 25);
   System.out.println(affectedRows);
   //使用useGeneratedKey,將主鍵注入到user.id中
   User u = new User("張三", 17);
   affectedRows = dao.insert_useGeneratedKey(u);
   System.out.println(affectedRows + " " + u.getId());
   //使用selectKey執行在插入以前或以後執行查詢語句
   affectedRows = dao.insert_selectKey(u);
   System.out.println(affectedRows + " " + u.getId());
   //使用selectKey註解的方式
   affectedRows = dao.insert_selectKeyAnotation(u);
   System.out.println(affectedRows + " " + u.getId());
   session.commit();
   List<User> a = dao.getAll();
   a.forEach(System.out::println);
}
}

6、insert()函數返回值

以下代碼,insert()函數的返回值爲int類型,表示affectedRows,即受影響的行數,若是成功插入返回1,若是不成功插入,返回0。對於一切寫操做(insert,update,delete),返回值都是affectedRows。

@Insert("insert into user(name,age) value(#{name},#{age})")
int insert(@Param("name") String name, @Param("age") int age);

7、關於@SelectKey

關於insert()有一種需求很常見:如何肯定插入數據的主鍵。對於MySQL中的自增類型主鍵,無需提供主鍵能夠直接插入。仍是以insert()函數爲例,這個SQL語句沒有提供主鍵,主鍵是自增類型能夠自動生成。

@Insert("insert into user(name,age) value(#{name},#{age})")
int insert(@Param("name") String name, @Param("age") int age);

下面介紹一個重要註解@SelctKey(statement="SQL語句",keyProperty="將SQL語句查詢結果存放到keyProperty中去",before="true表示先查詢再插入,false反之",resultType=int.class)
其中:

  • statement是要運行的SQL語句,它的返回值經過resultType來指定
  • before表示查詢語句statement運行的時機
  • keyProperty表示查詢結果賦值給代碼中的哪一個對象,keyColumn表示將查詢結果賦值給數據庫表中哪一列
  • keyProperty和keyColumn都不是必需的,有沒有均可以
  • before=true,插入以前進行查詢,能夠將查詢結果賦給keyProperty和keyColumn,賦給keyColumn至關於更改數據庫
  • befaore=false,先插入,再查詢,這時只能將結果賦給keyProperty
  • 賦值給keyProperty用來「讀」數據庫,賦值給keyColumn用來寫數據庫
  • selectKey的兩大做用:一、生成主鍵;二、獲取剛剛插入數據的主鍵。
  • 使用selectKey,而且使用MySQL的last_insert_id()函數時,before必爲false,也就是說必須先插入而後執行last_insert_id()才能得到剛剛插入數據的ID。

注意:

  • 該註解至關於XML配置中的<selectKey>的標籤
  • 與註解@Insert, @InsertProvider, @Update or @UpdateProvider搭配使用。在其餘方法上將被忽略。
  • 若是你指定了一個@SelectKey註解,而後Mybatis將忽略任何生成的key屬性經過設置@Options,或者配置屬性。
  • 屬性: statement是要執行的sql語句的字符串數組, keyProperty是須要更新爲新值的參數對象屬性, before能夠是true或者false分別表明sql語句應該在執行insert以前或者以後, resultType是keyProperty的Java類型, statementType是語句的類型,取Statement, PreparedStatement和CallableStatement對應的STATEMENT, PREPARED或者CALLABLE其中一個,默認是PREPARED。

一、舉一個before=true的例子,新插入數據的id是當前表中行的個數

當before=true,能夠經過SQL語句來填充insert語句中的某個參數,這個參數的名稱能夠經過keyProperty來指明。

@Insert("insert into user value(#{id},#{name},#{age})")
@SelectKey(statement="select count(1)from user", keyProperty="id", before=true, resultType=int.class)
int insert(@Param("name") String name, @Param("age") int age);

這個函數返回值是affectedRows,也就是插入成功返回1,插入失敗返回0。
以上這段代碼有一個大大的隱患萬萬不能用在生產環境中。這個隱患就是:不能經過count()來肯定id,多線程狀況下有可能產生衝突。解決方案:可使用UUID做爲主鍵。

二、before=false的狀況

注意keyProperty不能使基本類型,由於那樣賦值以後就找不到了(至關於傳值)
註解的方式

@Insert("insert into user(name,age) value(#{user.name},#{user.age})")
@SelectKey(statement = "select last_insert_id()", keyProperty = "user.id", before = false, resultType = int.class)
int insert_selectKeyAnotation(@Param("user") User user);

XML的方式

<insert id="insert_selectKey" parameterType="haha.User">
        <selectKey keyProperty="user.id" keyColumn="id" order="AFTER" resultType="int">
            SELECT last_insert_id()
        </selectKey>
        insert into user(name,age) VALUE (#{user.name},#{user.age})
    </insert>

三、在Oracle中使用SelectKey生成主鍵,一般是「先查詢獲得主鍵,再進行插入」

DUAL表是Oracle中的神奇的表
使用序列做爲主鍵

<insert id="insertSelective" parameterType="com.zehin.vpaas.base.domain.SfyHazardAnalysis">  
<selectKey resultType="java.lang.Integer" order="BEFORE" keyProperty="hazardId">  
        SELECT SEQUENCE_1.NEXTVAL FROM DUAL  
</selectKey>  
insert into SFY_HAZARD_ANALYSIS  
<trim prefix="(" suffix=")" suffixOverrides=",">  
    HAZARD_ID,  
    <if test="hazardTime != null"> HAZARD_TIME,</if>  
    <if test="hazardTitle != null"> HAZARD_TITLE, </if>  
    <if test="hazardMeasure != null"> HAZARD_MEASURE, </if>  
    <if test="buildId != null"> BUILD_ID, </if>  
</trim>  
<trim prefix=" values(" suffix=")" suffixOverrides=",">  
    #{hazardId,jdbcType=INTEGER},  
    <if test="hazardTime != null">#{hazardTime,jdbcType=VARCHAR},</if>  
    <if test="hazardTitle != null"> #{hazardTitle,jdbcType=VARCHAR},  </if>  
    <if test="hazardMeasure != null"> #{hazardMeasure,jdbcType=VARCHAR},  </if>  
    <if test="buildId != null"> #{buildId,jdbcType= INTEGER}, </if>  
</trim>  
lt;/insert>

使用GUID做爲主鍵

<insert id="insertUser" parameterType="com.danny.mybatis.po.User"> 
<selectKey keyProperty="userId" order="BEFORE" resultType="java.lang.Integer"> 
select SYS_GUID() as userId from DUAL
 </selectKey> 
insert into T_USER(userId,userName,birthday,sex,address) values (#{userId},#{userName},#{birthday},#{sex},#{address}) </insert>

四、使用useGeneratedKeys

<insert id="insert" parameterType="Spares"     
        useGeneratedKeys="true" keyProperty="id">    
        insert into spares(spares_id,spares_name,    
            spares_type_id,spares_spec)    
        values(#{id},#{name},#{typeId},#{spec})    
    </insert>
<insert id="insertUser" useGeneratedKeys="true" keyColumn="id">
        insert into user(name,age) VALUE (#{name},#{age})
    </insert>

8、獲取剛剛插入數據的主鍵

除了使用selectKey的方式獲取剛剛插入數據的主鍵,還有如下方案:
一、若是是MySQL,能夠用select last_insert_id()語句獲取新插入數據的主鍵。
二、若是主鍵類型是UUID,能夠直接在代碼中生成主鍵進行插入,這樣就不須要從數據庫中讀取主鍵了,主動權掌握在代碼手中。

9、參考資料

Mybatis官方文檔
CSDN偶爾記一下:mybatis如何獲取oracle新插入數據記錄的主鍵?

相關文章
相關標籤/搜索