一、創建Maven項目
在pom.xml中,添加mybatis依賴,mysql-jdbc依賴,把編譯版本改為1.8
你問,為啥mybatis不會自動依賴mysql-jdbc,需要手動寫明?答:因為mysql驅動是通過字符串動態加載的,這是一種“動態依賴”,Maven只能推導出“靜態依賴”。“動態依賴”是一種更加靈活的依賴。
Maven默認的Java版本是1.6,無法使用lambda表達式(1.8)和鑽石運算符(1.7)。
代碼片段:pom.xml
<?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,就可以開始編碼了。最終的目錄結構如下,下面讓我們來一步一步創建文件。
二、配置Mybatis
代碼片段:mybatis.xml
<?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.properties
username=root
password=haha
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai
把配置信息跟mybatis.xml分開的好處是:更清晰。mybatis屬於代碼區,config.properties改起來比較簡單。
三、創建實體類User
User有三個屬性:name,age和id,重寫toString()方法便於調試。
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);
}
}
相應的,在數據庫中建立一個表user
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
四、實現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語句有兩種方式:1、使用注解;2、使用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>
五、萬事俱備,只欠東風
編寫一個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);
}
}
六、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);
七、關於@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的兩大作用:1、生成主鍵;2、獲取剛剛插入數據的主鍵。
- 使用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。
1、舉一個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作為主鍵。
2、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>
3、在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>
4、使用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>
八、獲取剛剛插入數據的主鍵
除了使用selectKey的方式獲取剛剛插入數據的主鍵,還有以下方案:
1、如果是MySQL,可以用select last_insert_id()
語句獲取新插入數據的主鍵。
2、如果主鍵類型是UUID,可以直接在代碼中生成主鍵進行插入,這樣就不需要從數據庫中讀取主鍵了,主動權掌握在代碼手中。