zoukankan      html  css  js  c++  java
  • Mybatis中的@SelectKey注解

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

    九、参考资料

    Mybatis官方文档
    CSDN偶尔记一下:mybatis如何获取oracle新插入数据记录的主键?

  • 相关阅读:
    判断三点是否共线
    canvas判断点是否在路径内
    如何获取canvas当前的缩放值
    杨辉三角与排列组合数
    WEB 三维引擎在高精地图数据生产的探索和实践
    高精地图数据应用分发引擎建设实践
    视觉感知未来,高德数据采集模型部署实践!
    高德Serverless平台建设及实践
    高德客户端低代码系统架构实践
    数据人必读!玩转数据可视化用这个就够了——高德LOCA API 2.0升级来袭!
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/6835301.html
Copyright © 2011-2022 走看看