Mybatis面试篇-1

66 阅读10分钟

1,Mybatis间简介

2,Mybtis 是如何将sql 执行结果封装为目标对象并返回的? 都有哪些映射形式?

3,如何执行批量插入

4,如何获取自动升程的主键值

5,Mybatis 动态sql 有什么用? 执行原理? 有哪些动态sql

6,Xml 映射文件中,除了常见的select/insert/update/delete 还有哪些标签?

7,在mapper 中如何传递多个参数

8,Mybatis 与JPA区别?与Hibernate 区别

9,Mybatis 有哪些sql 编写形式?

10,Mybatis支持哪些传参数的方式

11,Mybatis # 与 $传参的区别

12,Mybatis 可以映射到枚举类吗

13,Mybatis 怎么封装动态sql

14,Mybatis trim 标签有什么用

一、Mybatis简介

MyBatis 是一款优秀的持久层框架,它简化了 Java 应用程序与数据库之间的交互操作,以下是对 MyBatis 框架的详细介绍:

框架概述

  • MyBatis 最初是作为 Apache 软件基金会的一个开源项目,它的前身是 iBATIS。MyBatis 通过将 Java 对象与 SQL 语句之间进行映射,使得开发人员可以更加方便地进行数据库操作,而无需编写大量的 JDBC 代码。它提供了一种灵活、高效且易于维护的方式来实现数据持久化,广泛应用于各种 Java Web 应用和企业级应用开发中。

核心组件

  • SqlSessionFactoryBuilder:它是用于创建SqlSessionFactory的构建器。通过读取配置文件或配置类,使用SqlSessionFactoryBuilder可以构建出SqlSessionFactory实例。例如:

    String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

  • SqlSessionFactory:是 MyBatis 的核心工厂类,负责创建SqlSession实例。它是线程安全的,可以在整个应用程序中共享。通常在应用启动时创建一次,并在需要进行数据库操作时获取SqlSession对象。例如:

    SqlSession sqlSession = sqlSessionFactory.openSession();

  • SqlSession:是 MyBatis 与数据库交互的主要接口,提供了执行 SQL 语句、管理事务等功能。通过SqlSession可以调用各种数据库操作方法,如selectOneselectListinsertupdatedelete等。例如:

    try { SqlSession sqlSession = sqlSessionFactory.openSession(); User user = sqlSession.selectOne("com.example.dao.UserDao.selectUserById", 1); sqlSession.close(); } catch (Exception e) { e.printStackTrace(); }

  • Mapper 接口和 Mapper XML 文件:Mapper 接口定义了与数据库交互的方法签名,而 Mapper XML 文件则实现了这些方法对应的 SQL 语句。MyBatis 会根据 Mapper 接口和 Mapper XML 文件之间的映射关系,自动将接口方法的调用转换为对应的 SQL 语句执行。

    // UserDao接口 public interface UserDao { User selectUserById(int id); }

    // UserMapper.xml文件 SELECT * FROM users WHERE id = #{id}

工作原理

  • 当应用程序启动时,首先通过SqlSessionFactoryBuilder读取配置文件创建SqlSessionFactory实例。然后,根据需要从SqlSessionFactory中获取SqlSession对象。
  • 当调用SqlSession的方法时,MyBatis 会根据方法的名称和参数,查找对应的 Mapper XML 文件中的 SQL 语句,并将参数进行处理后,通过 JDBC 驱动发送到数据库执行。
  • 数据库执行完 SQL 语句后,将结果返回给 MyBatis,MyBatis 会根据结果集和 Mapper XML 文件中定义的结果映射关系,将结果转换为 Java 对象,并返回给调用者。
  • 最后,在完成数据库操作后,需要关闭SqlSession对象,以释放相关的资源。

配置文件

  • MyBatis 的配置文件主要用于配置数据库连接信息、Mapper 接口和 Mapper XML 文件的位置、全局配置参数等。以下是一个简单的mybatis-config.xml配置文件示例:

在上述配置文件中,定义了数据库连接的相关信息,如驱动类、URL、用户名和密码等,以及指定了 Mapper XML 文件的位置。

优点

  • 灵活性高:MyBatis 允许开发人员手动编写 SQL 语句,能够根据具体的业务需求进行高度定制化的数据库操作,对复杂查询和性能优化提供了更大的空间。
  • 性能较好:通过预编译 SQL 语句和对缓存的支持,可以有效提高数据库操作的性能,减少数据库的访问次数,尤其在处理大数据量和高并发场景时表现出色。
  • 易于学习和使用:对于有一定 SQL 基础的开发人员来说,MyBatis 的学习曲线相对较平缓,其配置文件和 API 都比较简洁明了,易于理解和掌握。
  • 与现有系统集成方便:MyBatis 可以与各种 Java 框架和技术无缝集成,如 Spring、Spring Boot 等,能够方便地融入到现有的项目中,不会对项目的架构产生较大的影响。

缺点

  • 需要编写较多的 SQL 语句:与一些全自动化的 ORM 框架相比,MyBatis 需要开发人员手动编写大量的 SQL 语句,对于一些简单的增删改查操作可能会增加一定的开发工作量。
  • 对对象关系映射的支持有限:虽然 MyBatis 也支持对象关系映射,但相对来说不如一些专门的 ORM 框架强大,在处理复杂的对象关系时可能需要额外的配置和处理。
  • 动态 SQL 的编写较为复杂:尽管 MyBatis 提供了动态 SQL 的功能,但在编写复杂的动态 SQL 语句时,可能会导致 Mapper XML 文件中的 SQL 语句变得冗长和难以维护。

应用场景

  • MyBatis 适用于对 SQL 语句的灵活性和性能要求较高的场景,如互联网应用中的大数据量查询、复杂业务逻辑的数据库操作等。特别是在需要与现有数据库架构紧密结合,或者需要对数据库进行深度优化的项目中,MyBatis 是一个不错的选择。

二、Mybtis 是如何将sql 执行结果封装为目标对象并返回的? 都有哪些映射形式?

MyBatis 在执行 SQL 语句后,会通过多种方式将结果集封装为目标对象并返回,主要有以下几种映射形式:

基于 resultMap 的映射

  • 定义 resultMap:在 Mapper XML 文件中,通过<resultMap>标签定义结果集与目标对象之间的映射关系。例如:

在上述示例中,id属性为结果集映射的标识,type指定了映射的目标 Java 对象类型。通过<id><result>标签分别定义了主键和普通属性的映射关系,将查询结果中的user_id列映射到User对象的id属性,user_name列映射到name属性,user_age列映射到age属性。

  • 使用 resultMap:在<select>等 SQL 语句标签中,通过resultMap属性指定使用的结果集映射。例如:

    SELECT * FROM users WHERE id = #{id}

当执行该查询语句时,MyBatis 会根据resultMap中定义的映射关系,将查询结果中的列值封装到对应的User对象属性中,并返回该对象。

自动映射

  • MyBatis 默认开启自动映射功能,它会尝试将查询结果集中的列名与目标对象的属性名进行自动匹配,并将列值赋给对应的属性。如果列名与属性名完全一致,或者通过下划线命名风格与驼峰命名风格的转换能够匹配上,MyBatis 就会自动进行映射。例如,查询结果集中有列user_iduser_nameuser_age,而目标对象User有属性idnameage,MyBatis 会自动将列值赋给对应的属性,无需显式定义resultMap
  • 可以通过配置autoMappingBehavior属性来调整自动映射的行为,有PARTIAL(默认值,只对非嵌套的结果集进行自动映射)、FULL(对所有结果集进行自动映射)和NONE(关闭自动映射)三种取值。

嵌套映射

  • 一对一嵌套映射:当查询结果中包含一对一的关联关系时,可以使用嵌套映射来将关联对象也封装到目标对象中。例如,一个User对象关联一个Address对象,查询用户信息及对应的地址信息时,可以在UserResultMap中定义嵌套映射,如下所示:

    SELECT * FROM addresses WHERE id = #{id}

在上述示例中,通过<result>标签的select属性指定了一个查询语句,用于根据关联的address_id查询对应的Address对象,并将其封装到User对象的address属性中。

  • 一对多嵌套映射:对于一对多的关联关系,同样可以使用嵌套映射。例如,一个User对象有多个Order对象,查询用户信息及对应的订单信息时,可以在UserResultMap中定义一对多的嵌套映射,如下所示:

    SELECT * FROM orders WHERE user_id = #{id}

在上述示例中,通过<collection>标签定义了一对多的关系,property指定了在User对象中存储订单列表的属性名,ofType指定了集合中元素的类型,select属性指定了根据用户 ID 查询订单的语句,在子查询的resultMap中定义了订单对象的属性映射。

鉴别器映射

  • 鉴别器映射用于根据查询结果中的某个列值来决定使用不同的结果集映射。例如,一个Employee表中包含不同类型的员工,如普通员工、经理等,查询员工信息时,可以根据employee_type列的值来使用不同的结果集映射,如下所示:

在上述示例中,通过<discriminator>标签根据employee_type列的值来选择不同的结果集映射,对于不同类型的员工,可以定义不同的结果集映射来封装特定的属性。

MyBatis 通过以上多种映射形式,能够灵活地将 SQL 执行结果封装为目标对象,满足不同的业务需求和数据关联关系,为 Java 应用程序提供了强大而便捷的数据库持久化支持。

三、如何执行批量插入

MyBatis 提供了多种方式来执行批量插入操作,以下是一些常见的方法:

使用 foreach 标签

  • Mapper XML 配置:在 Mapper XML 文件中,使用<foreach>标签来遍历要插入的数据集合,并构建批量插入的 SQL 语句。示例如下:

    INSERT INTO users (name, age) VALUES (#{user.name}, #{user.age})

在上述示例中,userList是一个包含User对象的集合,通过<foreach>标签遍历该集合,将每个User对象的nameage属性值作为参数插入到users表中。其中,collection属性指定了要遍历的集合,item属性定义了集合中每个元素的别名,separator属性用于指定每个插入语句之间的分隔符。

  • Java 代码调用:在 Java 代码中,只需要调用对应的 Mapper 接口方法,并传入包含用户数据的集合即可执行批量插入操作。示例如下:

    List userList = new ArrayList<>(); // 填充userList数据

    UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.batchInsertUsers(userList);

使用 ExecutorType.BATCH

  • 配置 SqlSessionFactory:在 MyBatis 的配置文件中,可以设置ExecutorTypeBATCH,以启用批量执行模式。示例如下:

或者在获取SqlSession对象时动态指定ExecutorTypeBATCH,示例如下:

SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
  • 执行批量插入:在使用ExecutorType.BATCH模式的SqlSession时,MyBatis 会将多个插入操作缓存起来,然后一次性发送到数据库执行,从而提高批量插入的性能。示例如下:

    try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) { UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    List<User> userList = new ArrayList<>();
    // 填充userList数据
    
    for (User user : userList) {
        userMapper.insertUser(user);
    }
    
    sqlSession.commit();
    

    }

在上述示例中,通过循环调用insertUser方法将多个用户数据插入到数据库中,最后手动提交事务,此时 MyBatis 会将所有的插入操作作为一个批次发送到数据库执行。

基于注解的批量插入

  • 定义 Mapper 接口方法:使用 MyBatis 的注解来定义批量插入的方法,并在注解中指定插入语句和相关参数。示例如下:

    @Mapper public interface UserMapper { @Insert("" + "") int batchInsertUsers(@Param("userList") List userList); }

在上述示例中,使用@Insert注解定义了批量插入的 SQL 语句,其中通过<script>标签包含了动态的 SQL 片段,并使用@Param注解指定了方法参数与 SQL 语句中参数的绑定关系。

  • Java 代码调用:在 Java 代码中,直接调用 Mapper 接口的batchInsertUsers方法,并传入用户数据集合即可执行批量插入操作。示例如下:

    List userList = new ArrayList<>(); // 填充userList数据

    UserMapper userMapper = sqlSession.getMapper(UserMapper.class); int rowsAffected = userMapper.batchInsertUsers(userList);

性能考虑

  • 当执行大量数据的批量插入时,使用ExecutorType.BATCH模式通常会比普通的插入操作性能更好,因为它减少了与数据库的交互次数。然而,需要注意的是,批量插入的数据量也不宜过大,否则可能会导致内存溢出等问题。一般建议根据数据库的性能和硬件资源情况,合理调整批量插入的数量。
  • 另外,对于不同的数据库,其对批量插入的支持和性能表现也有所不同。例如,MySQL 有一些特定的配置参数和优化技巧可以进一步提高批量插入的效率,如调整innodb_buffer_pool_size等参数,以及使用LOAD DATA INFILE等更高效的导入方式等,可以根据具体使用的数据库进行相应的优化。

以上是 MyBatis 执行批量插入的几种常见方法,在实际应用中,可以根据具体的业务需求和项目情况选择合适的方式来实现批量插入操作,以提高数据插入的效率和性能。

四、如何获取自动生成的主键值

使用 useGeneratedKeys 和 keyProperty 属性

  • 配置 Mapper XML 文件:在 Mapper XML 文件的<insert>标签中,设置useGeneratedKeys="true"表示使用数据库自动生成的主键,同时通过keyProperty属性指定将自动生成的主键值设置到 Java 对象的哪个属性中。例如:

    INSERT INTO users (name, age) VALUES (#{name}, #{age})

在上述示例中,插入数据到users表后,数据库自动生成的主键值会被设置到User对象的id属性中。

  • Java 代码获取主键值:在执行插入操作后,可以直接从插入的对象中获取主键值。示例如下:

    User user = new User(); user.setName("John"); user.setAge(30);

    UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.insertUser(user);

    System.out.println("自动生成的主键值为:" + user.getId());

使用 selectKey 标签

  • 配置 Mapper XML 文件:当数据库的主键生成方式较为复杂,或者需要在插入数据后执行额外的查询来获取主键值时,可以使用<selectKey>标签。例如,对于某些数据库中使用序列来生成主键的情况,可以如下配置:

    INSERT INTO users (id, name, age) VALUES (#{id}, #{name}, #{age}) SELECT sequence_name.nextval FROM dual

在上述示例中,<selectKey>标签的keyProperty属性指定了将获取到的主键值设置到User对象的id属性中,resultType指定了主键值的类型,order="AFTER"表示在插入操作之后执行查询来获取主键值。

  • Java 代码获取主键值:与使用useGeneratedKeys

    User user = new User(); user.setName("Jane"); user.setAge(25);

    UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.insertUserWithSequence(user);

    System.out.println("自动生成的主键值为:" + user.getId());

对于支持 RETURNING 语句的数据库

  • 配置 Mapper XML 文件:某些数据库(如 PostgreSQL)支持在插入语句中使用RETURNING关键字来返回自动生成的主键值。MyBatis 也可以利用这一特性来获取主键值。例如:

    INSERT INTO users (name, age) VALUES (#{name}, #{age}) RETURNING id

在上述示例中,通过RETURNING id语句将自动生成的主键值返回。

  • Java 代码获取主键值:在执行插入操作后,MyBatis 会将返回的主键值封装到插入的对象中,或者作为方法的返回值返回,具体取决于 Mapper 接口方法的定义。示例如下:

    User user = new User(); user.setName("Tom"); user.setAge(28);

    UserMapper userMapper = sqlSession.getMapper(UserMapper.class); int generatedId = userMapper.insertUserAndReturnId(user);

    System.out.println("自动生成的主键值为:" + generatedId);

以上是在 MyBatis 中获取自动生成主键值的几种常见方法,不同的数据库和主键生成方式可能需要采用不同的配置来获取主键值,开发人员可以根据实际情况进行选择和调整。

五、mapper中如何传递多个参数

在 MyBatis 的 Mapper 中传递多个参数有多种方式,以下是一些常见的方法:

使用 Map 传递多个参数

  • Mapper XML 文件中的 SQL 语句:在 Mapper XML 文件中,可以使用#{}占位符来获取 Map 中的参数值。例如,查询年龄在指定范围内的用户信息,SQL 语句可以这样写:

    SELECT * FROM users WHERE age BETWEEN #{minAge} AND #{maxAge}
  • Java 代码中传递参数:在 Java 代码中,创建一个Map对象,并将需要传递的参数放入Map中,然后将Map作为参数传递给 Mapper 接口的方法。示例如下:

    Map<String, Object> params = new HashMap<>(); params.put("minAge", 20); params.put("maxAge", 30);

    UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List userList = userMapper.selectUsersByAgeRange(params);

使用 @Param 注解传递多个参数

  • Mapper 接口方法定义:在 Mapper 接口的方法中,使用@Param注解为每个参数指定一个名称,然后在 Mapper XML 文件中可以通过#{}占位符和参数名称来获取参数值。例如:

    public interface UserMapper { List selectUsersByNameAndAge(@Param("name") String name, @Param("age") int age); }

  • Mapper XML 文件中的 SQL 语句:对应的 SQL 语句如下:

    SELECT * FROM users WHERE name LIKE '%#{name}%' AND age = #{age}
  • Java 代码中传递参数:在 Java 代码中,直接按照参数顺序传递参数值即可。示例如下:

    UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List userList = userMapper.selectUsersByNameAndAge("John", 25);

使用 Java 对象传递多个参数

  • 创建参数对象:可以创建一个包含多个属性的 Java 对象,将需要传递的多个参数作为该对象的属性。例如,创建一个UserQueryParam对象:

    public class UserQueryParam { private String name; private int minAge; private int maxAge;

    // 省略getter和setter方法
    

    }

  • Mapper 接口方法定义:在 Mapper 接口中定义方法时,使用该参数对象作为方法的参数。例如:

    public interface UserMapper { List selectUsersByQueryParam(UserQueryParam param); }

  • Mapper XML 文件中的 SQL 语句:在 Mapper XML 文件中,可以通过#{}占位符和参数对象的属性名来获取参数值。例如:

    SELECT * FROM users WHERE name LIKE '%#{param.name}%' AND age BETWEEN #{param.minAge} AND #{param.maxAge}
  • Java 代码中传递参数:在 Java 代码中,创建UserQueryParam对象,并设置相应的属性值,然后将该对象作为参数传递给 Mapper 接口的方法。示例如下:

    UserQueryParam param = new UserQueryParam(); param.setName("John"); param.setMinAge(20); param.setMaxAge(30);

    UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List userList = userMapper.selectUsersByQueryParam(param);

使用数组或集合传递多个参数

  • Mapper 接口方法定义:在 Mapper 接口中定义方法时,可以使用数组或集合作为参数。例如,查询多个指定 ID 的用户信息:

    public interface UserMapper { List selectUsersByIds(int[] ids); }

  • Mapper XML 文件中的 SQL 语句:在 Mapper XML 文件中,可以使用foreach标签来遍历数组或集合中的参数值。例如:

    SELECT * FROM users WHERE id IN #{id}
  • Java 代码中传递参数:在 Java 代码中,创建相应的数组或集合,并填充参数值,然后将其作为参数传递给 Mapper 接口的方法。示例如下:

    int[] ids = {1, 2, 3, 4, 5};

    UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List userList = userMapper.selectUsersByIds(ids);

以上是在 MyBatis 的 Mapper 中传递多个参数的几种常见方法,开发人员可以根据实际情况选择合适的方式来传递参数,以满足不同的业务需求。

六、Mybatis 动态sql 有什么用? 执行原理?有哪些动态sql

MyBatis 的动态 SQL 是一种强大的功能,它允许开发人员根据不同的条件动态地生成 SQL 语句,从而提高 SQL 的灵活性和复用性,更好地满足复杂的业务需求。以下是对 MyBatis 动态 SQL 的详细介绍:

动态 SQL 的作用

  • 满足复杂多变的业务需求:在实际的业务场景中,查询条件、更新字段等往往是根据用户的输入或业务规则动态变化的。动态 SQL 可以根据不同的条件拼接出不同的 SQL 语句,以适应各种复杂的查询和操作需求。例如,根据用户选择的不同筛选条件生成相应的查询语句,或者根据不同的业务场景动态地更新部分字段。
  • 提高代码的复用性和可维护性:通过动态 SQL,可以将一些通用的 SQL 片段进行抽取和复用,减少代码的冗余。同时,当业务需求发生变化时,只需要修改动态 SQL 中的相关条件判断和逻辑,而无需对大量的静态 SQL 语句进行修改,提高了代码的可维护性。
  • 提升性能优化的灵活性:动态 SQL 可以根据实际情况灵活地选择查询的列、表连接条件、索引等,从而更好地进行性能优化。例如,在某些情况下可以根据查询条件动态地选择使用不同的索引,或者根据数据量的大小动态地调整查询的范围和方式,以提高查询的效率。

动态 SQL 的执行原理

  • 当 MyBatis 执行动态 SQL 语句时,首先会解析 Mapper XML 文件中的动态 SQL 标签,根据传入的参数值和标签中的条件判断来动态地生成最终要执行的 SQL 语句。
  • MyBatis 会将动态生成的 SQL 语句中的参数进行处理,如设置参数值、进行类型转换等,然后通过 JDBC 驱动将处理后的 SQL 语句发送到数据库执行。
  • 数据库执行完 SQL 语句后,将结果返回给 MyBatis,MyBatis 再根据 Mapper XML 文件中定义的结果映射关系,将结果集转换为 Java 对象,并返回给调用者。

常见的动态 SQL 标签及用法

  • if 标签:用于根据条件判断是否添加相应的 SQL 片段。例如,根据用户是否输入姓名来动态地添加查询条件:

    SELECT * FROM users WHERE 1 = 1 AND name LIKE '%#{name}%'

在上述示例中,如果name参数不为null,则会添加AND name LIKE '%#{name}%'到查询语句中。

  • choose、when、otherwise 标签:类似于 Java 中的switch语句,用于从多个条件中选择一个满足的条件来添加相应的 SQL 片段。例如:

    SELECT * FROM users WHERE 1 = 1 AND name LIKE '%#{name}%' AND age = #{age} AND gender = '男'

在上述示例中,会根据nameage参数的情况选择其中一个条件添加到查询语句中,如果nameage都为null,则会添加AND gender = '男'

  • where 标签:用于简化动态 SQL 中的条件判断和WHERE子句的拼接。它会自动去除条件中的第一个ANDOR关键字,并在条件都不满足时自动去除WHERE关键字。例如:

    SELECT * FROM users AND name LIKE '%#{name}%' AND age = #{age}

使用where标签后,无需再手动添加WHERE 1 = 1等条件来避免WHERE关键字的错误添加。

  • set 标签:用于在动态更新语句中根据条件设置更新的字段。它会自动去除更新字段中的最后一个逗号,并在没有更新字段时自动去除SET关键字。例如:

    UPDATE users name = #{name}, age = #{age} WHERE id = #{id}
  • foreach 标签:用于遍历数组、集合等类型的参数,并将其元素作为参数值添加到 SQL 语句中。例如,批量插入用户数据:

    INSERT INTO users (name, age) VALUES (#{user.name}, #{user.age})

在上述示例中,通过foreach标签遍历userList集合,将每个User对象的nameage属性值作为参数插入到users表中。

MyBatis 的动态 SQL 功能为开发人员提供了一种灵活、高效的方式来处理复杂的 SQL 语句生成需求,能够更好地满足各种业务场景下的数据操作要求,提高了代码的质量和开发效率。

七、Xml 映射文件中,除了常见的select/insert/update/delete 还有哪些标签?

在 MyBatis 的 Mapper XML 映射文件中,除了常见的<select><insert><update><delete>标签外,还有以下一些重要的标签:

基础配置标签

  • mapper:是 Mapper XML 文件的根标签,用于定义命名空间,将 Mapper 接口与 XML 文件中的 SQL 语句进行关联。例如:

  • resultMap:用于定义结果集与 Java 对象之间的映射关系,当查询结果的列名与 Java 对象的属性名不一致,或者需要进行复杂的对象关系映射时,可以使用该标签来配置映射规则。例如:

动态 SQL 标签

  • if:用于条件判断,根据传入参数的值决定是否包含相应的 SQL 片段。例如:

    SELECT * FROM users WHERE 1 = 1 AND name LIKE '%#{name}%'
  • choosewhenotherwise:类似于 Java 中的switch语句,从多个条件中选择一个满足的条件来添加相应的 SQL 片段。例如:

    SELECT * FROM users WHERE 1 = 1 AND name LIKE '%#{name}%' AND age = #{age} AND gender = '男'
  • where:用于简化动态 SQL 中的条件判断和WHERE子句的拼接,自动去除条件中的第一个ANDOR关键字,并在条件都不满足时自动去除WHERE关键字。例如:

    SELECT * FROM users AND name LIKE '%#{name}%' AND age = #{age}
  • set:用于在动态更新语句中根据条件设置更新的字段,自动去除更新字段中的最后一个逗号,并在没有更新字段时自动去除SET关键字。例如:

    UPDATE users name = #{name}, age = #{age} WHERE id = #{id}
  • foreach:用于遍历数组、集合等类型的参数,并将其元素作为参数值添加到 SQL 语句中。例如:

    INSERT INTO users (name, age) VALUES (#{user.name}, #{user.age})

缓存配置标签

  • cache:用于配置命名空间级别的二级缓存,通过该标签可以设置缓存的一些属性,如缓存的过期时间、缓存的刷新策略等。例如:

上述配置表示使用 LRU(最近最少使用)算法来清除缓存,缓存的刷新间隔为 60 秒,缓存的最大容量为 512 个对象,并且缓存中的对象是只读的。

其他标签

  • sql:用于定义可复用的 SQL 片段,通过id属性为片段命名,然后在其他 SQL 语句中可以使用<include>标签来引用该片段。例如:

    user_id, user_name, user_age SELECT FROM users
  • selectKey:用于在插入数据后获取自动生成的主键值,或者在执行其他 SQL 操作后获取特定的返回值。例如:

    INSERT INTO users (id, name, age) VALUES (#{id}, #{name}, #{age}) SELECT sequence_name.nextval FROM dual

这些标签共同构成了 MyBatis 强大而灵活的 Mapper XML 映射功能,使得开发人员能够更加方便地进行数据库操作和对象关系映射,满足各种复杂的业务需求。

八,MyBatis 与Hibernate JPA 的区别

MyBatis、Hibernate 和 JPA 都是 Java 中常用的持久层框架,它们在功能、使用方式、性能等方面存在一些区别,以下是详细介绍:

功能特性

  • MyBatis:MyBatis 更注重 SQL 语句的灵活性和可定制性,它允许开发人员直接编写 SQL 语句,能够对复杂的查询和性能优化提供更精细的控制。同时,MyBatis 提供了动态 SQL 的功能,可以根据不同的条件动态地生成 SQL 语句,以满足多样化的业务需求。
  • Hibernate:Hibernate 是一个功能强大的 ORM 框架,它提供了对象关系映射的全面支持,能够自动将 Java 对象与数据库表进行映射,无需开发人员编写大量的 SQL 语句。Hibernate 还支持多种数据库方言,具有良好的数据库兼容性和可移植性。此外,Hibernate 提供了一级缓存和二级缓存机制,能够有效地提高数据访问性能。
  • JPA:JPA(Java Persistence API)是 Java EE 标准的一部分,它定义了一组用于对象关系映射和持久化的接口和规范。JPA 的主要目标是提供一种统一的方式来访问不同类型的数据库,使得开发人员能够使用相同的 API 进行数据库操作,而无需关注具体的数据库实现。JPA 的实现通常基于 Hibernate 等 ORM 框架,因此继承了 Hibernate 的一些优点,如对象关系映射、缓存机制等。

SQL 语句编写

  • MyBatis:开发人员需要手动编写 SQL 语句,这使得开发人员能够根据具体的业务需求和数据库结构进行高度定制化的查询和操作。虽然 MyBatis 提供了一些辅助工具和动态 SQL 标签来简化 SQL 语句的编写,但总体来说,对开发人员的 SQL 编写能力要求较高。
  • Hibernate:Hibernate 通过 HQL(Hibernate Query Language)或 Criteria API 来进行查询操作,HQL 类似于 SQL,但它是面向对象的查询语言,开发人员可以使用对象和属性来代替表和列进行查询。此外,Hibernate 还支持使用原生 SQL 语句进行查询,但这种方式相对较少使用,因为它会破坏 Hibernate 的对象关系映射和可移植性。
  • JPA:JPA 使用 JPQL(Java Persistence Query Language)进行查询操作,JPQL 与 HQL 类似,也是一种面向对象的查询语言。JPA 还支持使用 Criteria API 来构建动态查询,提供了一种类型安全的方式来查询数据。与 Hibernate 不同的是,JPA 的查询语句是基于接口和注解的,更加符合 Java EE 的编程规范。

数据库移植性

  • MyBatis:由于 MyBatis 的 SQL 语句是手动编写的,因此在不同的数据库之间移植时,可能需要对 SQL 语句进行一些修改和调整,以适应不同数据库的语法和特性。不过,MyBatis 提供了一些数据库方言的配置,可以在一定程度上简化数据库移植的工作。
  • Hibernate:Hibernate 具有较好的数据库移植性,它通过配置不同的数据库方言,可以自动生成适应不同数据库的 SQL 语句。这使得开发人员在切换数据库时,只需要修改少量的配置信息,而无需大量修改代码。
  • JPA:JPA 作为 Java EE 的标准,具有更好的数据库移植性,它的 API 和查询语言是与具体的数据库无关的,开发人员可以使用相同的代码访问不同类型的数据库。JPA 的实现框架通常会提供对各种常见数据库的支持,确保了应用程序在不同数据库环境下的兼容性。

性能表现

  • MyBatis:在性能方面,MyBatis 的优势在于其对 SQL 语句的优化和定制能力。开发人员可以根据具体的业务场景和数据库结构,编写高效的 SQL 语句,并通过调整缓存策略等方式来提高性能。由于 MyBatis 对 SQL 语句的直接控制,使得它在处理复杂查询和大数据量时,能够提供较好的性能表现。
  • Hibernate:Hibernate 的性能表现取决于多种因素,如对象关系映射的复杂度、缓存的使用情况等。在一般情况下,Hibernate 的性能是可以满足大多数应用程序的需求的。然而,由于 Hibernate 的自动对象关系映射和缓存机制的复杂性,在某些情况下可能会出现性能问题,需要开发人员进行仔细的性能优化和调优。
  • JPA:JPA 的性能表现与具体的实现框架有关,由于 JPA 通常基于 Hibernate 等 ORM 框架实现,因此其性能特点与 Hibernate 类似。在使用 JPA 时,开发人员可以通过合理配置缓存、优化查询语句等方式来提高性能。

开发效率

  • MyBatis:对于熟悉 SQL 语句的开发人员来说,MyBatis 的学习曲线相对较平缓,开发效率较高。由于可以直接编写 SQL 语句,开发人员能够快速地实现各种复杂的数据库操作。然而,对于不熟悉 SQL 的开发人员来说,可能会面临一些挑战,需要花费更多的时间来学习和掌握 SQL 的编写技巧。
  • Hibernate:Hibernate 的开发效率取决于对其对象关系映射和查询语言的熟悉程度。一旦开发人员掌握了 Hibernate 的使用方法,能够快速地进行对象持久化和数据查询操作,无需编写大量的 SQL 语句。但是,Hibernate 的配置和使用相对较为复杂,需要开发人员对其有深入的理解,否则可能会出现一些配置错误和性能问题。
  • JPA:JPA 的开发效率与 Hibernate 类似,它提供了一种标准化的方式来进行对象关系映射和持久化操作,使得开发人员能够更加专注于业务逻辑的实现。JPA 的注解和接口使得代码更加简洁和易于维护,提高了开发效率。

适用场景

  • MyBatis:适用于对 SQL 语句的灵活性和性能要求较高的场景,如互联网应用中的大数据量查询、复杂业务逻辑的数据库操作等。特别是在需要与现有数据库架构紧密结合,或者需要对数据库进行深度优化的项目中,MyBatis 是一个不错的选择。
  • Hibernate:适用于对对象关系映射和数据库移植性要求较高的场景,如企业级应用开发中,需要快速实现对象持久化和数据访问层的搭建,并且对数据库的兼容性有较高要求的项目。
  • JPA:适用于遵循 Java EE 标准的企业级应用开发,特别是在需要使用标准化的持久化 API 和跨数据库移植的项目中。JPA 的使用可以提高代码的可维护性和可移植性,降低开发成本和风险。

MyBatis、Hibernate 和 JPA 各有优缺点,开发人员应根据具体的项目需求、团队技术栈和性能要求等因素,选择合适的持久层框架来实现数据持久化和访问。

九、MyBatis 有哪几种sql 编写形式

XML 配置文件编写 SQL

  • 基本 SQL 语句编写:在 Mapper XML 文件中,使用<select><insert><update><delete>等标签来编写基本的 SQL 语句。例如:

    SELECT * FROM users WHERE id = #{id} INSERT INTO users (name, age) VALUES (#{name}, #{age}) UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id} DELETE FROM users WHERE id = #{id}

这种方式可以清晰地将 SQL 语句与 Java 代码分离,便于维护和管理,尤其适合复杂的 SQL 查询和操作。

  • 动态 SQL 编写:MyBatis 提供了丰富的动态 SQL 标签,如<if><choose><when><otherwise><where><set><foreach>等,用于根据不同的条件动态生成 SQL 语句。例如:

    SELECT * FROM users AND name LIKE '%#{name}%' AND age = #{age} UPDATE users name = #{name}, age = #{age} WHERE id = #{id} INSERT INTO users (name, age) VALUES (#{user.name}, #{user.age})

通过动态 SQL,可以根据传入的参数值动态地拼接 SQL 语句,满足复杂多变的业务需求,提高代码的复用性和灵活性。

基于注解编写 SQL

  • 简单 SQL 注解:可以使用@Select@Insert@Update@Delete等注解直接在 Mapper 接口的方法上编写 SQL 语句。例如:

    @Mapper public interface UserMapper { @Select("SELECT * FROM users WHERE id = #{id}") User selectUserById(int id);

    @Insert("INSERT INTO users (name, age) VALUES (#{name}, #{age})")
    int insertUser(User user);
    
    @Update("UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id}")
    int updateUser(User user);
    
    @Delete("DELETE FROM users WHERE id = #{id}")
    int deleteUserById(int id);
    

    }

这种方式将 SQL 语句与 Mapper 接口方法紧密结合,使代码更加简洁直观,适用于简单的 SQL 操作。

  • 动态 SQL 注解:对于动态 SQL,MyBatis 也提供了相应的注解支持。可以使用@SelectProvider@InsertProvider@UpdateProvider@DeleteProvider等注解,通过指定一个提供动态 SQL 语句的类和方法来实现动态 SQL 的编写。例如:

    @Mapper public interface UserMapper { @SelectProvider(type = UserSqlProvider.class, method = "selectUsersByCondition") List selectUsersByCondition(UserQueryParam param);

    @InsertProvider(type = UserSqlProvider.class, method = "batchInsertUsers")
    int batchInsertUsers(List<User> userList);
    

    }

    public class UserSqlProvider { public String selectUsersByCondition(UserQueryParam param) { StringBuilder sql = new StringBuilder("SELECT * FROM users WHERE 1 = 1"); if (param.getName()!= null) { sql.append(" AND name LIKE '%").append(param.getName()).append("%'"); } if (param.getAge()!= null) { sql.append(" AND age = ").append(param.getAge()); } return sql.toString(); }

    public String batchInsertUsers(Map<String, Object> params) {
        List<User> userList = (List<User>) params.get("userList");
        StringBuilder sql = new StringBuilder("INSERT INTO users (name, age) VALUES ");
        for (int i = 0; i < userList.size(); i++) {
            User user = userList.get(i);
            sql.append("(#{userList[").append(i).append("].name}, #{userList[").append(i).append("].age})");
            if (i < userList.size() - 1) {
                sql.append(",");
            }
        }
        return sql.toString();
    }
    

    }

通过这种方式,可以将动态 SQL 的逻辑封装在一个单独的类中,提高代码的可读性和可维护性。

混合编写 SQL

在实际开发中,也可以根据具体的需求和项目情况,将 XML 配置文件编写 SQL 和基于注解编写 SQL 的方式结合使用。例如,对于一些复杂的查询和动态 SQL,可以使用 Mapper XML 文件来编写,以充分发挥其灵活性和可维护性;而对于一些简单的增删改操作,可以使用注解的方式来编写,使代码更加简洁明了。

MyBatis 提供了多种灵活的 SQL 编写形式,开发人员可以根据自己的喜好、项目的复杂程度以及团队的开发规范等因素,选择合适的方式来编写 SQL 语句,以提高开发效率和代码质量。

十、Mybatis 支持哪些传递参数的方法

单个参数传递

  • 基本数据类型参数:可以直接将基本数据类型(如intStringdouble等)作为参数传递给 Mapper 接口的方法。在 Mapper XML 文件中,使用#{}占位符来获取参数值。例如:

    public interface UserMapper { User selectUserById(int id); }

    SELECT * FROM users WHERE id = #{id}
  • 包装类型参数:对于包装类型(如IntegerString等)的参数传递方式与基本数据类型类似。例如:

    public interface UserMapper { User selectUserByAge(Integer age); }

    SELECT * FROM users WHERE age = #{age}
  • POJO 类参数:可以将一个包含多个属性的 POJO 类作为参数传递给 Mapper 接口方法。在 Mapper XML 文件中,通过#{属性名}的方式获取 POJO 类中的属性值。例如:

    public class UserQueryParam { private String name; private int age;

    // 省略getter和setter方法
    

    }

    public interface UserMapper { List selectUsersByParam(UserQueryParam param); }

    SELECT * FROM users WHERE name LIKE '%#{param.name}%' AND age = #{param.age}

多个参数传递

  • 使用 @Param 注解:在 Mapper 接口的方法中,使用@Param注解为每个参数指定一个名称,然后在 Mapper XML 文件中通过#{参数名}来获取参数值。例如:

    public interface UserMapper { List selectUsersByNameAndAge(@Param("name") String name, @Param("age") int age); }

    SELECT * FROM users WHERE name LIKE '%#{name}%' AND age = #{age}
  • 使用 Map 传递:创建一个Map对象,将多个参数放入Map中,并为每个参数指定一个键,然后将Map作为参数传递给 Mapper 接口方法。在 Mapper XML 文件中,通过#{键名}来获取参数值。例如:

    public interface UserMapper { List selectUsersByMap(Map<String, Object> params); }

    SELECT * FROM users WHERE name LIKE '%#{name}%' AND age = #{age}

在 Java 代码中传递参数时:

Map<String, Object> params = new HashMap<>();
params.put("name", "John");
params.put("age", 25);

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.selectUsersByMap(params);

数组和集合参数传递

  • 数组参数传递:可以将数组作为参数传递给 Mapper 接口方法,在 Mapper XML 文件中使用foreach标签来遍历数组元素,并将其作为参数值使用。例如:

    public interface UserMapper { List selectUsersByIds(int[] ids); }

    SELECT * FROM users WHERE id IN #{id}
  • 集合参数传递:传递集合参数的方式与传递数组类似,同样使用foreach标签来遍历集合元素。例如:

    public interface UserMapper { List selectUsersByNames(List names); }

    SELECT * FROM users WHERE name IN #{name}

复杂对象参数传递

  • 自定义对象嵌套:当参数对象中包含其他自定义对象时,可以通过多层级的属性获取来使用参数值。例如:

    public class Address { private String city; private String street;

    // 省略getter和setter方法
    

    }

    public class User { private String name; private int age; private Address address;

    // 省略getter和setter方法
    

    }

    public interface UserMapper { List selectUsersByAddress(User user); }

    SELECT * FROM users WHERE age = #{user.age} AND address.city = #{user.address.city}
  • 使用 ResultMap 传递参数:在一些特殊情况下,可以将查询结果的ResultMap作为参数传递给另一个查询。例如,先查询出一部分用户信息,然后将其作为参数传递给另一个查询来获取更详细的用户信息。这种方式相对较少使用,但在某些复杂的业务场景中可能会有帮助。

MyBatis 提供了丰富多样的参数传递方法,能够满足各种不同的业务需求和复杂的查询条件,开发人员可以根据具体情况选择合适的参数传递方式来实现数据库操作。

十一、MyBatis的$ 与#传参的区别

在 MyBatis 中,#$都可以用于在 SQL 语句中传递参数,但它们之间存在一些重要的区别,主要体现在以下几个方面:

语法和用途

  • ##是 MyBatis 的预编译占位符,在实际执行 SQL 语句时,MyBatis 会将#所代表的参数值替换为实际的值,并进行预编译处理,以防止 SQL 注入攻击。例如:

    SELECT * FROM users WHERE id = #{id}

在上述示例中,#{id}是一个预编译占位符,MyBatis 会将其替换为实际的参数值,并对整个 SQL 语句进行预编译,然后再发送到数据库执行。

  • $$是字符串拼接符号,MyBatis 会将$所代表的参数值直接拼接到 SQL 语句中,而不会进行预编译处理。例如:

    SELECT * FROM users WHERE name LIKE '%$ {name}$%'

在上述示例中,$ {name}$会被直接替换为参数值,然后将拼接后的 SQL 语句直接发送到数据库执行。

安全性

  • #:由于#采用预编译的方式,会对参数值进行转义等处理,因此可以有效地防止 SQL 注入攻击。即使传入的参数值中包含特殊字符或 SQL 关键字,MyBatis 也会将其作为普通的字符串值进行处理,而不会影响 SQL 语句的结构和执行逻辑,从而提高了系统的安全性。
  • $:使用$进行参数传递时,由于是直接将参数值拼接到 SQL 语句中,如果传入的参数值未经过严格的校验和过滤,就可能导致 SQL 注入攻击。例如,如果用户输入的参数值中包含恶意的 SQL 语句片段,那么这些片段就会被直接拼接到 SQL 语句中,从而改变 SQL 语句的原意,导致数据泄露、数据篡改等安全问题。

性能表现

  • #:因为#使用预编译的方式,数据库会对预编译后的 SQL 语句进行缓存,下次执行相同结构的 SQL 语句时,只需要传入不同的参数值即可,无需再次进行编译,从而提高了查询的性能。特别是在执行多次相同结构的 SQL 语句时,预编译的优势更加明显。
  • $:由于$每次都需要将参数值直接拼接到 SQL 语句中,然后再发送到数据库进行编译和执行,因此无法享受数据库的预编译缓存机制带来的性能提升。在大量重复执行相同结构的 SQL 语句时,$的性能相对较差。

适用场景

  • #:适用于大多数情况下的参数传递,特别是当参数值可能包含用户输入或不可信的数据时,应该优先使用#来保证系统的安全性和性能。例如,查询条件中的数值型参数、字符串型参数等都适合使用#来传递。

  • $:在某些特定的场景下,$可能会更有用。例如,当需要动态地指定表名、列名或排序字段等 SQL 语句的组成部分时,可以使用$来传递参数。但在使用$时,必须确保传入的参数值是经过严格校验和过滤的,以避免 SQL 注入攻击。例如:

    SELECT * FROM users ORDER BY $ {orderByColumn}

在上述示例中,$ {orderByColumn}可以动态地指定排序字段,但在实际使用时,需要对orderByColumn参数值进行严格的校验,确保其只包含合法的列名。

综上所述,在使用 MyBatis 时,应该优先选择#来传递参数,以保证系统的安全性和性能。只有在确实需要动态指定 SQL 语句的组成部分,并且能够确保参数值的安全性时,才考虑使用$。同时,无论使用哪种方式传递参数,都应该对用户输入的数据进行充分的校验和过滤,以防止潜在的安全风险。

十二、Mybatis 可以映射到枚举类吗

MyBatis 是可以映射到枚举类的,以下是几种常见的映射方式:

使用 ordinal () 方法映射

  • 这种方式是默认的枚举映射方式,它使用枚举常量在枚举类中的顺序值(从 0 开始)作为数据库中的存储值。例如,有一个表示用户状态的枚举类:

    public enum UserStatus { INACTIVE, ACTIVE, SUSPENDED }

  • 在 Mapper XML 文件中,可以像普通属性一样进行映射,例如:

  • 当插入或更新数据时,MyBatis 会将枚举常量的顺序值存储到数据库中;在查询时,会将数据库中的整数值转换为对应的枚举常量。

使用 toString () 方法映射

  • 可以通过配置typeHandler来指定使用枚举类的toString()方法进行映射,即把枚举常量的名称作为字符串存储到数据库中。首先需要创建一个自定义的类型处理器,例如:

    import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import org.apache.ibatis.type.TypeHandler;

    import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;

    @MappedJdbcTypes(JdbcType.VARCHAR) @MappedTypes(UserStatus.class) public class UserStatusTypeHandler implements TypeHandler {

    @Override
    public void setParameter(PreparedStatement ps, int i, UserStatus parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter.toString());
    }
    
    @Override
    public UserStatus getResult(ResultSet rs, String columnName) throws SQLException {
        return UserStatus.valueOf(rs.getString(columnName));
    }
    
    @Override
    public UserStatus getResult(ResultSet rs, int columnIndex) throws SQLException {
        return UserStatus.valueOf(rs.getString(columnIndex));
    }
    
    @Override
    public UserStatus getResult(CallableStatement cs, int columnIndex) throws SQLException {
        return UserStatus.valueOf(cs.getString(columnIndex));
    }
    

    }

  • 然后在 Mapper XML 文件中的resultMapinsertupdate等语句中指定使用这个类型处理器,例如:

    INSERT INTO users (name, age, status) VALUES (#{name}, #{age}, #{status,typeHandler=com.example.UserStatusTypeHandler})
  • 这样,在插入和查询数据时,就会使用枚举常量的名称进行存储和转换。

自定义映射方式

  • 如果上述两种方式都不能满足需求,还可以实现完全自定义的映射逻辑。例如,假设数据库中存储的用户状态值是012,分别对应INACTIVEACTIVESUSPENDED,但不想使用ordinal()方法,而是希望有更明确的映射关系,可以创建如下的类型处理器:

    import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import org.apache.ibatis.type.TypeHandler;

    import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;

    @MappedJdbcTypes(JdbcType.INTEGER) @MappedTypes(UserStatus.class) public class CustomUserStatusTypeHandler implements TypeHandler {

    @Override
    public void setParameter(PreparedStatement ps, int i, UserStatus parameter, JdbcType jdbcType) throws SQLException {
        int statusValue;
        if (parameter == UserStatus.INACTIVE) {
            statusValue = 0;
        } else if (parameter == UserStatus.ACTIVE) {
            statusValue = 1;
        } else {
            statusValue = 2;
        }
        ps.setInt(i, statusValue);
    }
    
    @Override
    public UserStatus getResult(ResultSet rs, String columnName) throws SQLException {
        int statusValue = rs.getInt(columnName);
        if (statusValue == 0) {
            return UserStatus.INACTIVE;
        } else if (statusValue == 1) {
            return UserStatus.ACTIVE;
        } else {
            return UserStatus.SUSPENDED;
        }
    }
    
    @Override
    public UserStatus getResult(ResultSet rs, int columnIndex) throws SQLException {
        int statusValue = rs.getInt(columnIndex);
        if (statusValue == 0) {
            return UserStatus.INACTIVE;
        } else if (statusValue == 1) {
            return UserStatus.ACTIVE;
        } else {
            return UserStatus.SUSPENDED;
        }
    }
    
    @Override
    public UserStatus getResult(CallableStatement cs, int columnIndex) throws SQLException {
        int statusValue = cs.getInt(columnIndex);
        if (statusValue == 0) {
            return UserStatus.INACTIVE;
        } else if (statusValue == 1) {
            return UserStatus.ACTIVE;
        } else {
            return UserStatus.SUSPENDED;
        }
    }
    

    }

  • 同样,在 Mapper XML 文件中指定使用这个自定义的类型处理器来进行枚举类的映射:

    INSERT INTO users (name, age, status) VALUES (#{name}, #{age}, #{status,typeHandler=com.example.CustomUserStatusTypeHandler})

通过以上几种方式,MyBatis 可以方便地实现与枚举类的映射,开发人员可以根据具体的业务需求和数据库设计选择合适的映射方式。

十三、mysql 怎么封装动态sql

在 MySQL 中,封装动态 SQL 主要是通过使用存储过程或者函数来实现的,以下是具体的介绍:

使用存储过程封装动态 SQL

  • 创建存储过程

    • 首先,在 MySQL 中可以使用CREATE PROCEDURE语句来创建一个存储过程。例如,创建一个根据用户输入的条件查询用户信息的存储过程:

    DELIMITER CREATEPROCEDUREdynamicqueryusers(INconditionVARCHAR(255))BEGINSET@sql=CONCAT(SELECTFROMusersWHERE,condition);PREPAREstmtFROM@sql;EXECUTEstmt;DEALLOCATEPREPAREstmt;ENDCREATE PROCEDURE dynamic_query_users(IN condition VARCHAR(255)) BEGIN SET @sql = CONCAT('SELECT * FROM users WHERE ', condition); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; END DELIMITER ;

在上述示例中:

  • DELIMITER $$:将语句分隔符暂时修改为$$,因为在存储过程内部可能会有分号;,为了避免与默认分隔符冲突,先修改分隔符。

  • CREATE PROCEDURE dynamic_query_users(IN condition VARCHAR(255)):创建一个名为dynamic_query_users的存储过程,它接受一个名为condition的输入参数,参数类型为VARCHAR(255)

  • SET @sql = CONCAT('SELECT * FROM users WHERE ', condition):通过CONCAT函数将固定的查询语句部分SELECT * FROM users WHERE和传入的条件参数condition拼接成一个完整的动态 SQL 语句,并将其赋值给用户变量@sql

  • PREPARE stmt FROM @sql:使用PREPARE语句将动态 SQL 语句进行预编译,生成一个可执行的语句对象stmt

  • EXECUTE stmt:执行预编译后的语句对象stmt,从而执行动态 SQL 查询。

  • DEALLOCATE PREPARE stmt:在查询执行完毕后,使用DEALLOCATE PREPARE语句释放预编译语句所占用的资源。

  • DELIMITER ;:最后将语句分隔符恢复为默认的分号;

  • 调用存储过程

    • 当需要查询用户信息时,可以调用这个存储过程并传入相应的条件参数。例如:

    CALL dynamic_query_users('age > 25');

上述调用会执行存储过程dynamic_query_users,并传入条件age > 25,从而查询出年龄大于 25 岁的用户信息。

使用函数封装动态 SQL

  • 创建函数

    • 同样可以使用CREATE FUNCTION语句来创建一个函数来封装动态 SQL。例如,创建一个根据用户输入的姓名返回用户信息的函数:

    DELIMITER CREATEFUNCTIONdynamicgetuserinfo(nameVARCHAR(255))RETURNSSETOFusersBEGINSET@sql=CONCAT(SELECTFROMusersWHEREnameLIKEPREPAREstmtFROM@sql;EXECUTEstmt;DEALLOCATEPREPAREstmt;RETURNQUERY(SELECTFROMusersWHEREnameLIKEENDCREATE FUNCTION dynamic_get_user_info(name VARCHAR(255)) RETURNS SET OF users BEGIN SET @sql = CONCAT('SELECT * FROM users WHERE name LIKE ''%', name, '%'''); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; RETURN QUERY (SELECT * FROM users WHERE name LIKE '%' + name + '%'); END DELIMITER ;

在上述示例中:

  • DELIMITER $$DELIMITER ;的作用与创建存储过程时相同,是为了避免分隔符冲突。

  • CREATE FUNCTION dynamic_get_user_info(name VARCHAR(255)) RETURNS SET OF users:创建一个名为dynamic_get_user_info的函数,它接受一个名为name的输入参数,参数类型为VARCHAR(255),并且函数返回类型为SET OF users,表示返回一个用户信息的集合。

  • SET @sql = CONCAT('SELECT * FROM users WHERE name LIKE ''%', name, '%'''):通过CONCAT函数将查询语句部分和传入的姓名参数拼接成动态 SQL 语句,并赋值给@sql。这里需要注意对单引号的处理,因为在 SQL 语句中字符串需要用单引号括起来,所以在拼接时要正确处理单引号的嵌套。

  • PREPARE stmt FROM @sqlEXECUTE stmtDEALLOCATE PREPARE stmt的作用与创建存储过程时相同,分别是预编译、执行和释放资源。

  • RETURN QUERY (SELECT * FROM users WHERE name LIKE '%' + name + '%'):在执行完动态 SQL 查询后,通过RETURN QUERY将查询结果作为函数的返回值返回。

  • 调用函数

    • 当需要根据姓名获取用户信息时,可以调用这个函数。例如:

    SELECT * FROM dynamic_get_user_info('John');

上述调用会执行函数dynamic_get_user_info,并传入姓名参数John,从而获取姓名中包含John的用户信息。

通过使用存储过程和函数,在 MySQL 中可以有效地封装动态 SQL,使得动态 SQL 的使用更加方便、规范,同时也提高了代码的可维护性和复用性。不过在使用过程中,需要注意对输入参数的校验和处理,以避免出现 SQL 注入等安全问题。

十四、Mybatis trim 标签有什么用?

在 MyBatis 中,<trim>标签是一种用于动态处理 SQL 语句的标签,它主要用于在生成的 SQL 语句中去除多余的关键字或字符,或者添加必要的前缀和后缀,具有以下几个常见的用途:

去除多余的逗号

  • 在动态更新语句中,当根据不同条件更新多个字段时,如果使用<if>标签来判断是否更新某个字段,可能会导致生成的 SQL 语句中出现多余的逗号。例如:

    UPDATE users SET name = #{name}, age = #{age}, email = #{email} WHERE id = #{id}
  • 当只有nameage不为null时,生成的 SQL 语句会是UPDATE users SET name =?, age =?, WHERE id =?,其中在age =?后面多了一个逗号,这会导致 SQL 语法错误。使用<trim>标签可以解决这个问题,示例如下:

    UPDATE users name = #{name}, age = #{age}, email = #{email}, WHERE id = #{id}
  • <trim>标签的suffixOverrides属性指定了要去除的后缀字符,这里是逗号。这样,当生成 SQL 语句时,会自动去除每个<if>标签生成的语句末尾多余的逗号,确保生成的 SQL 语句语法正确。

去除多余的 AND 或 OR 关键字

  • 在动态查询语句中,当根据多个条件进行查询时,也可能会出现类似的问题。例如:

    SELECT * FROM users WHERE AND name LIKE '%#{name}%' AND age = #{age} AND gender = #{gender}
  • 当只有name不为null时,生成的 SQL 语句会是SELECT * FROM users WHERE AND name LIKE '%?%',其中多了一个AND关键字,导致 SQL 语法错误。使用<trim>标签可以去除多余的ANDOR关键字,示例如下:

    SELECT * FROM users AND name LIKE '%#{name}%' AND age = #{age} AND gender = #{gender}
  • <trim>标签的prefixOverrides属性指定了要去除的前缀字符,这里是ANDOR后面跟着一个空格。这样,当生成 SQL 语句时,会自动去除每个<if>标签生成的语句开头多余的ANDOR关键字,确保生成的 SQL 语句语法正确。

添加前缀和后缀

  • 除了去除多余的字符,<trim>标签还可以用于添加必要的前缀和后缀。例如,在插入数据时,如果表名是根据某个条件动态确定的,可以使用<trim>标签来添加表名的前缀或后缀。假设根据不同的用户类型将用户数据插入到不同的表中,示例如下:

    vip_users normal_users default_users
  • 在上述示例中,<trim>标签的prefix属性添加了INSERT INTO前缀,suffix属性添加了VALUES (#{name}, #{age})后缀,而中间通过<choose><when><otherwise>标签动态地确定了要插入的表名。

综上所述,<trim>标签在 MyBatis 的动态 SQL 中起着重要的作用,能够帮助开发人员更灵活、准确地生成符合语法要求的动态 SQL 语句,避免因动态条件拼接而导致的 SQL 语法错误,提高了代码的健壮性和可维护性。