一、创建Maven项目
代码: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); } }
四、实现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,可以直接在代码中生成主键进行插入,这样就不需要从数据库中读取主键了,主动权掌握在代码手中。