深入理解MyBatis:优化CRUD操作与常见映射问题

本文深入探讨了MyBatis中如何优化CRUD操作,包括单个、多个参数的处理优化,以及使用对象封装参数。此外,文章还讲解了如何使用注解进行开发,插入数据后返回主键的机制以及解决列成员变量不一致问题的策略,如手动映射和驼峰映射。通过对MyBatis的深入理解,可以提升数据库操作的效率和代码的可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态SQL

基础SQL语句复习

整理后的基本SQL语句如下:

增加数据:

  1. 插入所有列的值:INSERT INTO 表名 VALUES (值1, 值2, ...)
  2. 插入指定列的值:INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...)

删除数据:

  1. 删除满足条件的数据:DELETE FROM 表名 WHERE 条件

修改数据:

  1. 更新数据:UPDATE 表名 SET 列1=值1, 列2=值2 WHERE 条件

查询数据:

  1. 基本查询:SELECT 列 FROM 表名 WHERE 条件
  2. 分组查询:SELECT 列 FROM 表名 WHERE 条件 GROUP BY 列名 HAVING 条件
  3. 排序查询:SELECT 列 FROM 表名 WHERE 条件 ORDER BY 列名 ASC/DESC
  4. 限制结果数量:SELECT 列 FROM 表名 WHERE 条件 LIMIT 10, 5

以上是基本的SQL语句,用于增加、删除、修改和查询数据。SQL语句是用来操作关系型数据库的标准语言,通过这些简单的语句可以完成对数据库的各种操作。需要根据具体情况来灵活应用,并可以结合更复杂的查询条件和子查询来实现更复杂的数据操作和分析。

使用mybatis进行CRUD

前提准备

1.配置pom文件

2.指定MyBatis的Mapper XML文件所在的位置

application.properties中,我们可以使用mybatis.mapper-locations属性来指定MyBatis的Mapper XML文件所在的位置。Mapper XML文件包含了SQL语句的定义,它们描述了数据库操作的具体逻辑。

具体配置如下:

mybatis.mapper-locations=classpath:mappers/*.xml

在上述配置中,mybatis.mapper-locations属性的值是一个包含通配符*的路径,表示Mapper XML文件所在的位置。在这个例子中,MyBatis会在classpath(类路径)下的mappers目录查找所有以.xml结尾的文件作为Mapper XML文件。这样配置后,MyBatis就能正确地找到并加载这些XML文件,并将其中的SQL语句注册到对应的Mapper接口中,供应用程序使用。

注意:classpath:是一个特殊的前缀,用于表示在类路径下查找资源文件。因此,classpath:mappers/*.xml表示在mappers目录下查找所有以.xml结尾的文件,该目录位于类路径下。如果你的Mapper XML文件在其他位置,需要根据实际情况进行相应的配置。

3.在启动类中添加注解

@MapperScan("com.example.mybatis2.mappers")

这行代码的作用是:

  1. 告知Spring Boot在com.example.mybatis2.mappers包及其子包下扫描所有的Mapper接口。
  2. 扫描到Mapper接口后,自动为其生成实现类,这些实现类中包含了MyBatis框架自动根据接口方法生成的SQL查询操作
  3. 注册这些Mapper接口和对应的实现类到Spring的应用上下文,使得其他组件可以方便地进行依赖注入并使用这些Mapper接口。

详细编写

xml:写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="com.example.mybatis_demo.mapper.UserMapper">
<!--编写查询sql语句-->
<!--    id对应的值为方法名,需要在namespace对应的接口(UserMapper接口)中添加方法-->
<!--    resultType是返回值,这里以User对象为存储容器 指定返回值为User-->
    <select id="findUserById" resultType="com.example.mybatis_demo.pojo.User">
            SELECT * FROM user;
    </select>
    <!--编写增加sql语句-->
    <insert id="addUser">
        insert into user values(null,'tom',12,1,'100086');
    </insert>
    <!--编写删除sql语句-->
    <delete id="deleteById">
        delete from user where id=6;
    </delete>
    <!--编写更新sql语句-->
    <update id="updateUser">
        update user set name='天龙' where id=3;
    </update>
</mapper>

接口:写方法

package com.example.mybatis_demo.mapper;

import com.example.mybatis_demo.pojo.User;

import java.util.List;

public interface UserMapper {
//    这里写xmlSQL语句所使用的语句
    List<User> findUserById();
    void addUser();
    void deleteById();
    void updateUser();
}

test文件:写测试方法

package com.example.mybatis_demo;

import com.example.mybatis_demo.mapper.UserMapper;
import com.example.mybatis_demo.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;
//    这里编写测试语句
//第一步 绑定启动类
@SpringBootTest(classes = MybatisDemoApplication.class)
//第二步使用@Autowired注入userMapper
class MybatisDemoApplicationTests {
@Autowired
    UserMapper userMapper;

//    @Test
//    void contextLoads() {
//        System.out.println(userMapper.findUserById(4));
//    }
//    第三步添加测试

    @Test
    void contextLoads() {
        List<User> user = userMapper.findUserById();
        System.out.println(user);
    }
    @Test
    void testAddUser(){
        userMapper.addUser();
    }
    @Test
    void testDeleteById(){
        userMapper.deleteById();
    }
    @Test
    void testUpdateUser(){
        userMapper.updateUser();
    }

}

优化CRUD操作参数处理

第一种:单个参数优化

1.1 设置参数为变量

在进行CRUD操作时,我们经常需要传递参数给MyBatis进行数据访问。为了优化参数传递的方式,我们可以将原来耦合的参数设置成#{参数名}方式。这种方式不仅使代码更清晰,还能提高代码的可维护性和灵活性。

1.2 修改Mapper.xml文件

修改Mapper.xml中的SQL语句,使用#{参数名}方式替代原来的硬编码参数。

    <!--编写删除sql语句-->
    <delete id="deleteById">
--         delete from user where id=6;
        delete from user where id=#{id};
    </delete>

1.3 Java文件更改

在Java的Mapper接口中,将原来的单个参数改为使用#{参数名}的方式。

void deleteById(@Param("id") Integer id);

1.4 测试类更改

测试类中的调用也需进行相应修改。

@Test
void testDeleteById() {
    userMapper.deleteById(2);
}

第二种:多个参数优化

2.1 设置多个参数为变量

在某些情况下,我们需要传递多个参数给MyBatis进行数据访问。同样,为了提高代码的可读性和维护性,我们可以使用#{参数名}方式传递多个参数。

2.2 修改Mapper.xml文件

修改Mapper.xml中的SQL语句,使用#{参数名}方式传递多个参数。

<update id="updateUser">
    update user set name=#{name} where id=#{id};
</update>

2.3 Java文件更改

在Java的Mapper接口中,使用@Param注解标明传入的参数名。

//    *记住了!!! 如果我们传递的参数是多个需要告诉mybatis 哪个参数是谁@Param("username") String username*
//*@param username
//    主要是针对老版本,新版本不需要添加注解
    void updateUser(@Param("name")String name,@Param("id") Integer id);

2.4 测试类更改

测试类中的调用方式保持不变。

@Test
void testUpdateUser() {
    userMapper.updateUser("Jack", 1);
}

注意:在MyBatis中,#{}是参数占位符,用于在SQL语句中替代具体的参数值。当我们在Mapper接口中定义了方法,并在XML文件中编写了SQL语句时,我们需要在SQL语句中指定参数的值,但由于参数值在运行时才能确定,所以我们使用#{}来表示该位置应该被参数值替代。

具体来说,在你提供的SQL语句中,name=#{name}中的两个name分别表示:

  1. #{name}:这个name是指Mapper接口中方法的参数名,例如在Java中的void updateUser(@Param("name") String name, @Param("id") Integer id);方法中,name是对应的参数名。
  2. name:这个name是指实体类(POJO)中的属性名,即User类中的name属性。

MyBatis会根据这两个name将实际的参数值与SQL语句进行绑定。在执行SQL语句时,会把#{name}替换为User对象中name属性对应的值。因此,实际上执行的SQL语句可能是类似于update user set name='Jack' where id=2;这样的形式,其中'Jack'就是User对象中name属性的值,而2则是updateUser方法中的id参数值。

这样的设计使得SQL语句与Java代码解耦,使得SQL语句更具可读性,并且在面对复杂的查询和动态SQL的情况下也能够方便地拼接参数值,提高了代码的灵活性和可维护性。

第三种:使用对象封装多个参数

3.1 设置参数为对象

为了进一步简化代码,我们可以将多个参数封装成一个对象,然后直接传递该对象给MyBatis进行数据访问。

3.2 修改Mapper.xml文件

修改Mapper.xml中的SQL语句,使用对象的属性作为参数。

<insert id="addUser">
    insert into user values(null,#{name},#{age},#{gender},#{phone})
</insert>

3.3 Java文件更改

在Java的Mapper接口中,使用对象作为参数。

void addUser(User user);

3.4 测试类更改

测试类中的调用方式也相应进行修改。

@Test
void testAddUser() {
    User user = new User();
    user.setAge(11);
    user.setName("Jack");
    user.setPhone("100086");
    user.setGender(1);
    userMapper.addUser(user);
}

通过以上的参数优化处理,我们可以更好地组织代码,使得MyBatis的数据访问更加高效和优雅。同时,优化后的代码具备更好的可维护性和可读性,使开发过程更加流畅。让我们在MyBatis开发中,从参数优化处理中受益!

注解开发

MyBatis支持使用注解进行开发,这样可以避免编写繁琐的Mapper XML文件,更加简洁和直观地定义SQL语句。在使用注解进行开发时,我们可以在Mapper接口的方法上使用注解来编写SQL语句,从而实现数据的增删改查操作。以下是使用注解进行MyBatis开发的步骤和示例:

  1. 添加依赖: 首先,在pom.xml文件中添加MyBatis的依赖,以及数据库驱动的依赖(如MySQL驱动)。

  2. 创建实体类: 创建与数据库表对应的实体类(POJO),并在实体类中添加注解或XML映射文件来描述字段与表的映射关系。

  3. 创建Mapper接口: 创建Mapper接口,用于定义数据访问的接口。在方法上使用注解来编写SQL语句。

  4. 配置MyBatis: 在Spring Boot的配置类中,使用@MapperScan注解指定Mapper接口所在的包路径,使得MyBatis能够扫描并注册Mapper接口的实现。

  5. 编写业务逻辑: 在Service层编写业务逻辑,注入Mapper接口的实例,通过调用Mapper接口的方法来访问数据库。

以下是一个示例代码,演示如何使用注解进行MyBatis开发:

  1. 创建实体类 User.java
public class User {
    private Integer id;
    private String username;
    private Integer age;
    // 其他属性和对应的getter/setter方法
}
  1. 创建Mapper接口 UserMapper.java
import org.apache.ibatis.annotations.*;

@Mapper
public interface UserMapper {

    @Insert("INSERT INTO user (username, age) VALUES (#{username}, #{age})")
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    void addUser(User user);

    @Delete("DELETE FROM user WHERE id = #{id}")
    void deleteUser(Integer id);

    @Update("UPDATE user SET username = #{username}, age = #{age} WHERE id = #{id}")
    void updateUser(User user);

    @Select("SELECT * FROM user WHERE id = #{id}")
    User getUserById(Integer id);
}
  1. 配置MyBatis:在Spring Boot的配置类中添加@MapperScan注解,指定Mapper接口的扫描路径。
import org.springframework.context.annotation.Configuration;
import org.mybatis.spring.annotation.MapperScan;

@Configuration
@MapperScan("com.example.mybatis2.mappers")
public class MyBatisConfig {
    // MyBatis相关配置
}
  1. 编写业务逻辑 UserService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private final UserMapper userMapper;

    @Autowired
    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public void addUser(User user) {
        userMapper.addUser(user);
    }

    public void deleteUser(Integer id) {
        userMapper.deleteUser(id);
    }

    public void updateUser(User user) {
        userMapper.updateUser(user);
    }

    public User getUserById(Integer id) {
        return userMapper.getUserById(id);
    }
}

通过上述步骤,我们就可以使用注解来定义SQL语句,实现对数据库的增删改查操作,而无需编写XML映射文件,使得代码更加简洁和直观。同时,MyBatis还提供了丰富的注解,可以满足各种复杂查询和数据库操作的需求。

插入数据返回主键

为什么需要设置插入数据后返回主键

  1. 获取插入数据的唯一标识: 数据库中的主键通常用于唯一标识一条记录。在插入数据后,我们通常希望获取刚插入数据的主键值,这样可以在后续的操作中方便地引用这条数据。
  2. 避免重复数据插入: 数据库表的主键通常是唯一的,插入数据时如果不返回主键,我们无法得知数据库中是否已经存在相同主键的记录,从而可能导致重复数据的插入。
  3. 数据关联和依赖: 在数据库中,表之间可能存在关联和依赖关系。比如,表A的主键被表B引用为外键,如果需要在表A插入一条记录,并将其主键作为外键插入到表B中,就需要先获取表A插入记录的主键值。
  4. 操作反馈和日志记录: 在实际应用中,我们通常需要记录插入数据的操作反馈或日志信息。通过返回主键值,我们可以方便地记录插入数据的成功或失败情况。
  5. 后续处理: 插入数据后,有时需要根据插入的数据进行一些后续处理。例如,插入一条订单记录后,需要在其他表中插入相关明细信息,此时就需要获取主键值。

如何使用

在MyBatis中,可以通过设置useGeneratedKeyskeyProperty属性来实现插入数据后返回主键的功能。这样做的好处是,在插入数据后,我们可以方便地获取生成的主键值,用于后续操作或记录。

第一种方式

  1. 在Mapper接口中的插入方法上添加@Options注解,并设置useGeneratedKeystruekeyProperty为要返回主键值的实体类属性。

  2. 在插入方法的SQL语句中使用数据库的主键生成机制来生成主键值。

创建Mapper接口

import org.apache.ibatis.annotations.*;

@Mapper
public interface UserMapper {

    @Insert("INSERT INTO user (username, age) VALUES (#{username}, #{age})")
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    void addUser(User user);

    // 其他方法...
}
  1. 在数据库中,确保user表的主键(假设为id列)是自增的或由数据库自动生成的。

  2. 在Service层或Controller层调用插入方法并传入User对象:

@Service
public class UserService {

    private final UserMapper userMapper;

    @Autowired
    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public void addUser(User user) {
        userMapper.addUser(user);
        // 插入后,可以直接获取生成的主键值
        Integer userId = user.getId();
        System.out.println("插入成功,用户ID为:" + userId);
    }

    // 其他方法...
}

在上述示例中,当执行addUser方法时,MyBatis会将插入后的主键值赋值给user对象的id属性,因为我们在@Options注解中设置了keyProperty="id",表示要将生成的主键值设置到id属性中。这样,我们就能够在插入数据后获取新插入数据的主键值。

第二种方式

是的,同样可以在Mapper XML文件中设置插入数据返回主键。在MyBatis的Mapper XML文件中,我们可以使用useGeneratedKeyskeyProperty属性来实现插入数据后返回主键的功能,与在注解中的设置类似。

以下是在Mapper XML文件中设置插入数据返回主键的示例:

  1. 创建Mapper XML文件 UserMapper.xml
<!-- UserMapper.xml -->
<mapper namespace="com.example.mybatis2.mappers.UserMapper">
  
  <!-- 添加用户,并返回自动生成的主键 -->
  <insert id="addUser" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user (username, age) VALUES (#{username}, #{age})
  </insert>
  
  <!-- 其他SQL语句... -->

</mapper>

在上述示例中,我们在<insert>元素上设置了useGeneratedKeys="true",表示使用数据库的主键生成机制来生成主键值。并且使用keyProperty="id",表示将生成的主键值设置到User实体类的id属性中。

  1. 创建Mapper接口 UserMapper,并在其中声明方法对应上述XML中的ID:
@Mapper
public interface UserMapper {

    void addUser(User user);

    // 其他方法...
}
  1. 在Service层或Controller层调用插入方法并传入User对象:
@Service
public class UserService {

    private final UserMapper userMapper;

    @Autowired
    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public void addUser(User user) {
        userMapper.addUser(user);
        // 插入后,可以直接获取生成的主键值
        Integer userId = user.getId();
        System.out.println("插入成功,用户ID为:" + userId);
    }

    // 其他方法...
}

在上述示例中,当执行addUser方法时,MyBatis会将插入后的主键值赋值给User对象的id属性,因为我们在XML文件中设置了keyProperty="id"。这样,我们就能够在插入数据后获取新插入数据的主键值。

列成员变量不一致问题(手动映射与驼峰映射)

手动映射结果和使用mybatis.configuration.map-underscore-to-camel-case=true是两种解决列成员变量不一致问题的方法,它们可以用于处理数据库表列名和Java对象成员变量名之间的命名规则不一致问题。

  1. 手动映射结果: 在MyBatis中,我们可以手动定义结果集映射关系,即将数据库表列名映射到Java对象成员变量名。这可以通过在Mapper接口的SQL语句中使用别名或在Mapper XML文件中使用<resultMap>来实现。

例如,假设数据库表中的列名为user_id,而Java对象的成员变量名为userId,我们可以在SQL语句中使用别名来映射:

@Select("SELECT user_id AS userId, username, age FROM user WHERE user_id = #{userId}")
User getUserById(Integer userId);

或者在Mapper XML文件中定义<resultMap>来手动映射结果集:

<mapper namespace="com.example.mybatis2.mappers.UserMapper">
    <resultMap id="userResultMap" type="User">
        <id property="userId" column="user_id" />
        <result property="username" column="username" />
        <result property="age" column="age" />
    </resultMap>

    <select id="getUserById" resultMap="userResultMap">
        SELECT user_id, username, age FROM user WHERE user_id = #{userId}
    </select>
</mapper>
  1. 使用驼峰映射mybatis.configuration.map-underscore-to-camel-case=true MyBatis提供了一个全局配置选项map-underscore-to-camel-case,用于自动将数据库表的下划线命名转换为Java对象的驼峰命名。

设置mybatis.configuration.map-underscore-to-camel-case=true后,MyBatis会在查询结果映射时,自动将数据库表列名的下划线转换为Java对象成员变量的驼峰命名。例如,数据库表的列名为user_id,则对应Java对象的成员变量名为userId

# application.properties
mybatis.configuration.map-underscore-to-camel-case=true

使用该配置后,我们可以简化Mapper接口方法和SQL语句的编写,不再需要手动映射结果。

@Mapper
public interface UserMapper {
    User getUserById(Integer userId);
}
<!-- UserMapper.xml -->
<mapper namespace="com.example.mybatis2.mappers.UserMapper">
    <select id="getUserById">
        SELECT user_id, username, age FROM user WHERE user_id = #{userId}
    </select>
</mapper>

总的来说,手动映射结果和使用mybatis.configuration.map-underscore-to-camel-case=true都是解决列成员变量不一致问题的有效方法。手动映射结果适用于需要更精细的映射控制或特殊映射情况,而使用全局配置则可以简化映射过程,使代码更加简洁和易读。根据具体情况,可以选择适合自己项目的方法。

《编译原理》是计算机科学中一门极为重要的课程,主要探讨如何将高级程序设计语言转换成机器可执行的指令。清华大学的张素琴教授在这一领域有着深厚的学术造诣,其编译原理课后习题答案对于学习者而言是非常珍贵的资源。这份压缩文件详细解析了课程中所涉及的概念、理论和方法的实践应用,目的是帮助学生更好地理解编译器设计的核心内容。 编译原理的核心知识点主要包括以下几点: 词法分析:作为编译过程的首要环节,词法分析器会扫描源代码,识别出一个个称为“标记”(Token)的最小语法单位。通常借助正则表达式来定义各种标记的模式。 语法分析:基于词法分析产生的标记流,语法分析器依据文法规则构建语法树。上下文无关文法(CFG)是编译器设计中常用的一种形式化工具。 语义分析:这一步骤用于理解程序的意义,确保程序符合语言的语义规则。语义分析可分为静态语义分析和动态语义分析,前者主要检查类型匹配、变量声明等内容,后者则关注运行时的行为。 中间代码生成:编译器通常会生成一种高级的中间表示,如三地址码或抽象语法树,以便于后续的优化和目标代码生成。 代码优化:通过消除冗余计算、改进数据布局等方式提升程序的执行效率,同时不改变程序的语义。 目标代码生成:根据中间代码生成特定机器架构的目标代码,这一阶段需要考虑指令集体系结构、寄存器分配、跳转优化问题。 链接:将编译后的模块进行合并,解决外部引用,最终形成一个可执行文件。 错误处理:在词法分析、语法分析和语义分析过程中,编译器需要能够检测并报告错误,例如语法错误、类型错误等。 张素琴教授的课后习题答案覆盖了上述所有核心知识点,并可能包含实际编程练习,比如实现简单的编译器或解释器,以及针对特定问题的解题策略。通过解答这些习题,学生可以加深对编译原理的理解,提升解决问题的能力,为今后参编译器开发或软件工程实践奠定坚实的基础。这份资源不仅是学习编译原理的有力辅助材料,也是
车辆路径问题(Vehicle Routing Problem, VRP)是物流运输领域中的一个重要优化问题,目标是规划一组最优车辆路线,确保所有客户点都被访问,同时使总行驶距离最小化。当引入时间窗约束(Time Windows)后,问题演变为带时间窗的车辆路径问题(Vehicle Routing Problem with Time Windows, VRPTW),其复杂性显著增加。在VRPTW中,每个客户点都有一个特定的服务时间窗口,车辆必须在该窗口内到达,否则无法满足客户需求。 本项目“VRPTW-ga”采用遗传算法(Genetic Algorithm, GA)来解决这一问题。遗传算法是一种基于生物进化原理的全局优化方法,通过模拟自然选择、基因重组和突变等过程,逐步生成近似最优解。在Python中实现遗传算法时,首先需要确定问题的编码方式。对于VRPTW,通常采用整数编码,每条路线用一串数字表示,数字的顺序对应车辆的访问顺序。接着,需要设计适应度函数(Fitness Function),用于评估每个个体(即一组路线)的优劣,通常以总行驶距离或总服务时间作为优化目标。遗传算法的基本流程如下:1. 初始化种群,随机生成一定数量的初始个体,代表不同的车辆路线;2. 适应度评估,计算每个个体的适应度值,适应度总行驶距离成反比;3. 选择操作,根据适应度值选择个体,常用方法包括轮盘赌选择和锦标赛选择等;4. 交叉操作,选择两个个体进行基因交叉,生成新的个体,VRPTW中可采用部分匹配交叉或顺序交叉等策略;5. 变异操作,对部分个体进行随机变异,调整其访问顺序,以维持种群多样性;6. 检查终止条件,若达到预设的迭代次数或适应度阈值,则停止算法,否则返回第2步继续执行。 在“VRPTW-ga-master”项目中,可能包含以下关键文件:problem.py用于定义车辆路径问题的具体
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值