mybatis-基础

相关依赖

<!--mysql依赖-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.15</version>
</dependency>
<!--mybatis依赖-->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.4.5</version>
</dependency>
<!--为了之后能方便测试-->
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

编写配置文件

<?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>
    <settings>
        <!--懒加载,没有真正用到XXX的时候,不会只是相关的SQL语句-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--对resultMap做一个映射子查询的自动映射-->
        <!--<setting name="autoMappingBehavior" value="FULL"/>-->
    </settings>
    <!-- 为 JavaBean 起类别名 -->
    <typeAliases>
        <!-- 指定一个包名起别名,将包内的 Java 类的类名作为类的类别名 -->
        <package name="cn.edu.guet.pojo" />
    </typeAliases>
    <!-- 配置 mybatis 运行环境 -->
    <environments default="development">
        <environment id="development">
            <!-- type="JDBC" 代表直接使用 JDBC 的提交和回滚设置 -->
            <transactionManager type="JDBC" />

            <!-- POOLED 表示支持 JDBC 数据源连接池 -->
            <!-- 数据库连接池,由 Mybatis 管理,数据库名是 mybatis,MySQL 用户名 root,密码为空 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/test2?useUnicode=true&amp;characterEncoding=gbk&amp;autoReconnect=true&amp;failOverReadOnly=false&amp;serverTimezone=UTC" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 通过 mapper 接口包加载整个包的映射文件,通俗来讲我们需要告诉 MyBatis 到哪里去找到这些语句 -->
        <package name="cn.edu.guet.mapper" />
    </mappers>
</configuration>
# 测试要的环境
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

搭建环境和编写映射sql

  • 实例化一个工厂建造类读取配置文件用来产生一个工厂类
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  • 从 SqlSessionFactory 中获取 SqlSession,SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
SqlSession session = sqlSessionFactory.openSession()
  • 编写要映射sql语句
<?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="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>
  • 创建一个BlogMapper的接口和方法名selectBlog的方法,用Session加载接口创建代理对象并调用方法,如下就会执行相关sql
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

用单例模式改造基础环境

package cn.edu.guet.singleton;

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;

/**
 * @author pangjian
 * @ClassName SessionFactory
 * @Description 构建操作mybatis的单例对象
 * @date 2021/7/5 10:49
 */

public class SessionFactory {

    private SqlSessionFactory sqlSessionFactory;
    private SqlSession sqlSession;

    /**
     * @Description:在程序运行时就为变量赋值,保证了该类只有一个实例,调用构造方法的同时也给sqlSessionFactory赋值,也保证了这个sqlSessionFactory也只有一个
     * @date 2021/7/5 11:22
    */
    private static SessionFactory instance = new SessionFactory();

    /**
     * @Description:构造器私有化,调用该方法就是给该类的sqlSessionFactory成员变量赋值
     * @return
     * @date 2021/7/5 10:56
    */
    private SessionFactory(){
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    /**
     * @Description:通过实例调用该方法返回一个Session对象
     * @return org.apache.ibatis.session.SqlSession
     * @date 2021/7/5 11:24
    */
    public SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }

    /**
     * @Description:返回该单例
     * @return cn.edu.guet.singleton.SessionFactory
     * @date 2021/7/5 11:24
    */
    public static SessionFactory getInstance(){
        return instance;
    }

}

简单测试

实体类

package cn.edu.guet.pojo;

/**
 * @author pangjian
 * @ClassName Dog
 * @Description TODO
 * @date 2021/7/5 11:55
 */
@Data
public class Dog {

    private String name;
    private Integer age;

}

数据库访问层接口

package cn.edu.guet.mapper;

import cn.edu.guet.pojo.Dog;

import java.util.List;

/**
 * @author pangjian
 * @Interface DogMapper
 * @Description TODO
 * @date 2021/7/5 11:58
 */

public interface DogMapper {

    List<Dog> selectAllDog();
	// 不同类型的多参数处理,name会映射到xml文件中的占位符,做为形参传过给xml
    Dog selectOneDog(@Param("name") String name,@Param("age") Integer age);

    void insertOneDog(Dog dog);

}

编写映射sql

<?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="cn.edu.guet.mapper.DogMapper">
    <select id="selectAllDog" resultType="Dog">
        select * from t_dog
    </select>

    <select id="selectOneDog" resultType="Dog">
        select * from t_dog where name=#{name} and age=#{age}
    </select>

    <insert id="insertOneDog" parameterType="Dog">
        insert into t_dog(name,age) values (name,age);
    </insert>
</mapper>
  • namespace是匿名空间,id是方法名,为了就是找到是哪一个类下的方法,给它做一个映射
  • resultType是返回类型,只能是简单数据类型或者引用类型,对应对象有着对象属性,则不能使用这个类型
  • parameterType是参数类型,如果是javabean,会把它的属性做一个映射到sql语句中的占位符,默认是属性名做形参

测试代码

package cn.edu.guet;


import cn.edu.guet.mapper.DogMapper;
import cn.edu.guet.pojo.Dog;
import cn.edu.guet.singleton.SessionFactory;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class Test {

    @org.junit.Test
    public void test1(){
        DogMapper dogMapper = SessionFactory.getInstance().getSqlSession().getMapper(DogMapper.class);
        List<Dog> list = dogMapper.selectAllDog();
        for (Dog dog : list){
            System.out.println(dog.getName());
        }
    }

    @org.junit.Test
    public void test2(){
        DogMapper dogMapper = SessionFactory.getInstance().getSqlSession().getMapper(DogMapper.class);
        Dog dog = dogMapper.selectOneDog("阿拉斯加",1);
        System.out.println(dog.getName());
    }

    @org.junit.Test
    public void test3(){
        SqlSession sqlSession = SessionFactory.getInstance().getSqlSession();
        DogMapper dogMapper = sqlSession.getMapper(DogMapper.class);
        Dog dog = new Dog("田园犬",2);
        dogMapper.insertOneDog(dog);
        sqlSession.commit();
    }
}

复杂测试(ResultMap的使用)

  • 当你查询出来的结果集字段不能一一映射到实体类的属性,那就要用到ResultMap了

实体类

数据库学生表里面存的是班级的id做为外键,而不是一个班级实例,我们就要连表查询,得到的结果集字段我们不仅仅要做映射到我们自定义的javabean中,首先我们要分析他们的关系。

package cn.edu.guet.pojo;

/**
 * @author pangjian
 * @ClassName Student
 * @Description 学生类,有一个班级属性
 * @date 2021/7/5 11:27
 */
@Data
public class Student {

    private Integer id;
    private String username;
    private String password;
    private Clazz clazz;   // 一个学生会属于一个班级

}
package cn.edu.guet.pojo;

import java.util.List;

/**
 * @author pangjian
 * @ClassName Class
 * @Description 班级类,有一个学生列表属性
 * @date 2021/7/5 11:27
 */
@Data
public class Clazz {

    private Integer id;
    private String className;
    private String area;
    private List<Student> students; // 一个班级会有很多个学生

}

有一个的问题(association)

/**
 * @Description:查询所以学生信息,每一个学生的班级信息也要包括
 * @return java.util.List<cn.edu.guet.pojo.Student>
 * @date 2021/7/5 16:06
*/
List<Student> selectAllStudent();

第一种方法

  • 学生肯定是属于一个班级,也就是多对一的问题,对于复杂属性类型,我们得用association去处理,查询出学生信息,包括了学生所在班级的信息,映射sql如下
<select id="selectAllStudent" resultMap="stu-cla">
    select * from t_student
</select>

<resultMap id="stu-cla" type="Student">
    <!--property是实体类的属性名,column是查询结果集字段名,可以是别名-->
    <id property="id" column="t_student.id"></id>
    <result property="username" column="username"></result>
    <result property="password" column="password"></result>
    <!--数据库的外键处理,property是javabean的属性名,也就是对象属性名clazz,column对应学生表的外键,javaType:实体类中的属性类型-->
    <association property="clazz" column="classid" javaType="Clazz" select="selectClass"></association>
</resultMap>
<!--子查询用的参数是通过association标签定义的classid值传过来的-->
<select id="selectClass" resultType="Clazz">
    select * from t_class where id=#{classid}
</select>

但这样会造成N+1问题,也就是对于select * from t_student执行一次,然后对结果集(结果集为N条记录)关联的查询会执行N次,我们可以采取懒加载去解决这个问题,当没有用到class信息,则不会执行子查询。也可以换一种方式去编写映射sql和resultMap。

<setting name="lazyLoadingEnabled" value="true"/>

第二种方法

<select id="selectAllStudent" resultMap="stu-cla">
    select *
    from t_student t
    left join t_class c
    on classid=c.id
</select>

<resultMap id="stu-cla" type="Student">
    <!--property是实体类的属性名,column是查询结果集字段名,可以是别名-->
    <id property="id" column="t_student.id"></id>
    <result property="username" column="username"></result>
    <result property="password" column="password"></result>
    <!--数据库的外键处理,property是javabean的属性名,也就是对象属性名clazz,column对应学生表的外键,javaType:实体类中的属性类型-->
    <association property="clazz" javaType="Clazz">
    	<!--对关联属性对象指定结果集字段映射-->
        <id property="id" column="t_class.id"></id>
        <result property="clazzName" column="name"></result>
        <result property="area" column="area"></result>
    </association>
</resultMap>

有多个的问题(collection)

/**
 * @Description: 根据id查询班级的信息,包括在这些班的所有学生
 * @Param id: 班级id
 * @return java.util.List<cn.edu.guet.pojo.Clazz>
 * @date 2021/7/5 15:54
*/
List<Clazz> selectOneClazz(@Param("id") Integer id);

第一种方法

<select id="selectOneClazz" resultMap="cla-stu">
    select *
    from t_class c
    left join t_student s
    on c.id=s.classid
    where c.id=#{id}
</select>
<resultMap id="cla-stu" type="Clazz">
    <id property="id" column="t_class.id"></id>
    <result property="className" column="classname"></result>
    <result property="area" column="area"></result>
    <!--由于属性类型是一个List,且泛型为Student,集合我们用ofType去表明List属性的泛型-->
    <collection property="students" ofType="Student" javaType="List">
        <result property="id" column="t_student.id"></result>
        <result property="username" column="username"></result>
        <result property="password" column="password"></result>
    </collection>
</resultMap>

第二种方法

<select id="selectOneClazz" resultMap="cla-stu">
    select * from t_class where id=#{id}
</select>
<resultMap id="cla-stu" type="Clazz">
    <id property="id" column="t_class.id"></id>
    <result property="className" column="classname"></result>
    <result property="area" column="area"></result>
    <!--由于属性类型是一个List,且泛型为Student,集合我们用ofType去指定-->
    <collection property="students" javaType="List" column="id" select="getAllStudentbyClass"></collection>
</resultMap>
<select id="getAllStudentbyClass" resultType="Student">
    <!--id形参是collection中column指定的名传过来的-->
    select * from t_student where classid=#{id}
</select>

#{} 和 ${}

区别:#{}是预编译,${}是拼接sql,#{}能防止sql注入

sql注入

利用了拼接sql的缺点,修改其执行sql语句的结构

1.登录如果用拼接sql形式去查询账号和密码是否正确

select * from `user` where
username = ${} and password = ${}
  • 预期开发人员逻辑:输入用户名和密码查询
select * from `user` where
username = 'admin' and password = '123456'
  • 黑客攻击站点sql注入逻辑(账号填写 ’ or 1 = 1 # )
# 并集有一个为true,就会查询出全部人的信息,这是我们不允许的
select * from `user` where
username = '' or 1 = 1 #' and password = '123456'

2.预编译sql形式去查询账号和密码是否正确

select * from `user` where
username = #{} and password = #{}

你可以理解为预编译为:指令和参数区别开来,在参数传过来之前预编译指令,告诉了DBMS这个sql已经确定好做什么了,你再传sql注入传指令,系统不会识别,而是当作成整个参数

# 并集有一个为true,就会查询出全部人的信息,这是我们不允许的
select * from `user` where
username = '' or 1 = 1 #' and password = '123456'
  • 当黑客再一次填写账号为 ’ or 1 = 1 #,系统会把这个做为参数去执行查询,而不会识别 or 这个关键字去改变原有的sql结构

#{}其实是用了PreparedStatement去预编译执行sql,而${}是用了Statement去拼接并执行sql


缓存

  • 什么是缓存

存在内存中的临时数据,将用户经常查询到的数据放到缓存中,用户去查询数据就不用从磁盘上查找了,直接从缓存上查询,提高了效率。

  • 为什么使用缓存

减少和数据库的交互次数,减少系统开销,提高系统效率

  • 什么数据能使用缓存

经常查询并且不经常改变的数据

一级缓存

默认情况只有一级缓存开启。(SqlSession级别,也就是一次会话期间,如果一次会话(创建到Close期间)进行相同的两次查询,第二次查询会走缓存)

@GetMapping("/queryUserList")
@Transactional
public List<User> queryUserList() {
    List<User> userList = userMapper.queryUserList();
    userList.stream().forEach(
            user -> System.out.println("第一次查询" + user)
    );
    List<User> userList1 = userMapper.queryUserList();
    userList1.stream().forEach(
            user -> System.out.println("第二次查询" + user)
    );
    return userList;
}

一定要加事务,由于使用了数据库连接池,默认每次查询完之后自动commite,这就导致两次查询使用的不是同一个sqlSessioin,根据一级缓存的原理,它将永远不会生效。当我们开启了事务,Spring将两次查询都在同一个sqlSession中,将事务边界上升到业务层,从而让第二次查询命中了一级缓存。

在这里插入图片描述

只走了一次sql


二级缓存

  • 工作机制

一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
如果当前会话关闭了,这个会话对应的一级缓存就没了;会话关闭了,一级缓存中的数据被保存到二级缓存中;新的会话查询信息,就可以从二级缓存中获取内容;不同的mapper查出的数据会放在自己对应的缓存(map)中;

在这里插入图片描述

1.先看二级缓存有没有想要的数据
2.再看一级缓存有没有想要的数据
3.查询数据库

  • 注意点

实体类要实现Serializable接口

# 在.xml文件中配置则是开启二级缓存
<cache/>

测试

@GetMapping("/queryUserList")
public List<User> queryUserList() {
    List<User> userList = userMapper.queryUserList();
    userList.stream().forEach(
            user -> System.out.println("第一次查询" + user)
    );
    return userList;
}

@GetMapping("/queryUserList1")
public List<User> queryUserList1(){
    List<User> userList1 = userMapper.queryUserList();
    userList1.stream().forEach(
            user -> System.out.println("第二次查询" + user)
    );
    return userList1;
}

进行两次查询,不同sqlSession,但也只进行了一次查询

在这里插入图片描述


自定义缓存

Mybatsi默认缓存对象PerpetualCache,是本地缓存,本地缓存不适合分布式环境,因为不同jvm环境不能共享。

手动注入redis操作实例

package com.pang.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @author pangjian
 * @ClassName ApplicationContextHolder
 * @Description TODO
 * @date 2021/8/28 18:49
 */
@Slf4j
@Component
public class ApplicationContextHolder implements ApplicationContextAware, DisposableBean {

    private static ApplicationContext applicationContext;

    /**
     * 获取存储在静态变量中的 ApplicationContext
     *
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        if (applicationContext == null) {
            throw new RuntimeException("applicationContext 属性未注入");
        }
        return applicationContext;
    }

    /**
     * 从静态变量 applicationContext 中获取 Bean,自动转型成所赋值对象的类型
     *
     * @param name
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name) {
        if (applicationContext == null) {
            throw new RuntimeException("applicationContext 属性未注入");
        }
        return (T) applicationContext.getBean(name);
    }

    /**
     * 从静态变量 applicationContext 中获取 Bean,自动转型成所赋值对象的类型
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        if (applicationContext == null) {
            throw new RuntimeException("applicationContext 属性未注入");
        }
        return applicationContext.getBean(clazz);
    }


    /**
     * 实现 DisposableBean 接口,在 Context 关闭时清理静态变量
     * @throws Exception
     */
    @Override
    public void destroy() throws Exception {
        log.debug("清除 SpringContext 中的 ApplicationContext: {}", applicationContext);
        applicationContext = null;
    }

    /**
     * 实现 ApplicationContextAware 接口,注入 applicationContext 到静态变量中
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContextHolder.applicationContext = applicationContext;
    }
}

采用redis自定义Mybatis的cache

package com.pang.cache;

import com.pang.config.ApplicationContextHolder;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.ibatis.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author pangjian
 * @ClassName MyBatisCache
 * @Description 在mybatis完成查询后,将数据缓存起来
 * @date 2021/8/28 17:49
 */
@Slf4j
public class MyBatisCache implements Cache {

    /**
     * @Description:id唯一标识,指的就是namespace
    */
    private String id;

    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    private RedisTemplate<String, Object> redisTemplate;

    public MyBatisCache() {
    }

    /**
     * @Description:项目启动时扫描XXXMapper.xml文件时,识别出<Cache type="xxx.xxx.Cache.MyBatisCache"></>
     * 就会调用有参构造,把namespace通过构造方法注入到MyBatisCache实例的id私有属性上
    */
    public MyBatisCache(String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        }
        this.id = id;
    }

    /**
     * @Description:Mybatis需要时会getId拿到该缓存实例的namespace
    */
    @Override
    public String getId() {
        return this.id;
    }

    /**
     * @Description: 当查询执行后,将结果缓存起来,我们自定义缓存实现用redis,所以把该数据自定义实现存储到redis里面
     * @Param k:此次查询的标识,其中包含sql语句(String)
     * @Param v:此次查询的结果数据
    */
    @Override
    public void putObject(Object k, Object v) {
        RedisTemplate<String, Object> redisTemplate = getRedisTemplate();
        redisTemplate.opsForValue().set(k.toString(), v);
    }

    /**
     * @Description: 通过key取到缓存数据
     * @Param k:
    */
    @Override
    public Object getObject(Object k) {
        RedisTemplate<String, Object> redisTemplate = getRedisTemplate();
        Object cache = redisTemplate.opsForValue().get(k.toString());
        if (cache == null) {
            log.debug("缓存未命中");
            return null;
        }
        return cache;
    }

    /**
     * @Description: 删除某一个缓存数据
     * @Param o:
    */
    @Override
    public Object removeObject(Object k) {
        try {
            RedisTemplate<String, Object> redisTemplate = getRedisTemplate();
            redisTemplate.delete(k.toString());
            log.debug("Remove cached query result from redis");
        } catch (Throwable t) {
            log.error("Redis remove failed", t);
        }
        return null;
    }

    @Override
    public void clear() {
        RedisTemplate redisTemplate = getRedisTemplate();
        redisTemplate.execute((RedisCallback) connection -> {
            connection.flushDb();
            return null;
        });
        log.debug("Clear all the cached query result from redis");
    }

    @Override
    public int getSize() {
        return 0;
    }

    @Override
    public ReadWriteLock getReadWriteLock() {
        return readWriteLock;
    }

    private RedisTemplate getRedisTemplate() {
        if (redisTemplate == null) {
            redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
        }
        return redisTemplate;
    }
}
# 全局开启二级缓存
mybatis:
  configuration:
    cache-enabled: true
# Mapper文件开启缓存
<cache type="com.pang.cache.MyBatisCache"/>

此次如果你的服务器宕机了,但redis缓存服务器没有宕机,那缓存依旧是存在的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MyBatis是一个流行的持久层框架,它可以将SQL语句和Java对象之间的映射关系定义在XML文件或注解中,并提供了丰富的查询语言和灵活的参数绑定方式。在使用MyBatis进行数据操作时,有时会遇到插入唯一异常的问题,下面让我们一起来看看如何解决这个问题。 1. 异常描述 当我们向数据库插入一条记录时,如果违反了唯一性约束,就会抛出插入唯一异常,如下所示: ``` ### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'xxx' for key 'name_unique' ### The error may involve com.example.mapper.UserMapper.insert-Inline ### The error occurred while setting parameters ### SQL: insert into user(name, age) values (?, ?) ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'xxx' for key 'name_unique' ``` 其中,'xxx'表示违反唯一性约束的值,'name_unique'表示违反唯一性约束的字段名。 2. 解决方法 为了避免插入唯一异常,我们可以采取以下两种解决方法: 2.1 使用INSERT IGNORE语句 在MySQL中,可以使用INSERT IGNORE语句来向表中插入记录,如果遇到违反唯一性约束的情况,就会忽略该记录,而不是抛出异常。因此,我们可以将MyBatis的插入语句改为INSERT IGNORE语句,如下所示: ``` <insert id="insertUser" parameterType="com.example.entity.User"> INSERT IGNORE INTO user(name, age) VALUES (#{name}, #{age}) </insert> ``` 2.2 使用ON DUPLICATE KEY UPDATE语句 在MySQL中,还可以使用ON DUPLICATE KEY UPDATE语句来向表中插入记录,如果遇到违反唯一性约束的情况,就会更新该记录,而不是抛出异常。因此,我们可以将MyBatis的插入语句改为ON DUPLICATE KEY UPDATE语句,如下所示: ``` <insert id="insertUser" parameterType="com.example.entity.User"> INSERT INTO user(name, age) VALUES (#{name}, #{age}) ON DUPLICATE KEY UPDATE age = #{age} </insert> ``` 其中,ON DUPLICATE KEY UPDATE语句指定了更新操作的字段和值,这里我们只更新了年龄字段。 以上就是解决MyBatis插入唯一异常的两种方法,根据具体情况选择适合自己的方法即可。 ### 回答2: 在使用MyBatis进行插入操作时,可能会遇到插入唯一异常。该异常通常是由于数据库表的唯一约束导致的。 当我们向数据库表插入数据时,如果违反了唯一约束,数据库将抛出异常,表示插入失败。常见的唯一约束有主键约束、唯一索引等。 解决这个问题的方法有两种: 1. 在程序中进行唯一性校验:在执行插入操作之前,可以先查询数据库中是否已存在相同的数据。如果已存在,则不进行插入操作,避免了唯一异常的发生。这种方法比较消耗数据库资源,但可以保证数据的唯一性。 2. 使用数据库的"insert ignore"或"insert on duplicate key update"语句:这种方法是在插入操作时,使用特殊的语句来处理唯一异常。"insert ignore"语句会即使发生唯一异常,也不会抛出异常,而是直接忽略这条插入数据;"insert on duplicate key update"语句则是在发生唯一异常时,执行更新操作。这种方法相对较为简洁高效,但需要根据数据库的不同进行调整。 总结来说,解决MyBatis插入唯一异常的方法有多种,可以通过程序中进行唯一性校验,或者使用特殊的数据库插入语句来处理。需要根据具体情况选择最合适的方法来解决唯一异常问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值