SpringBoot 数据访问(MyBatis)

SpringBoot 数据访问(MyBatis)

向 SQL 语句传参

#{} 形式

#{}:如果传过来的值是字符串类型。那两边会自动加上 单引号。当传递给 #{} 的参数值是非字符串类型(如整数、浮点数、布尔值等),MyBatis 不会为这些值添加引号。

#{ key }原理: 占位符 + 赋值 先 emp_id = ?,  然后 ? = 赋值

${} 形式

${}:它会直接将传过来变量的值替换到 SQL 语句中。

${ key }: 字符串拼接 " emp_id  = " + id

通常不会采用 ${} 的方式传值。一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,此时只能使用 ${}
.
结论:动态的是值就是用 #{},动态变化的部分是诸如容器名、标签、列名、SQL 关键字等非普通值的情况时,则需要使用 ${} 进行拼接

传入单个简单类型参数

单个简单类型参数,在 #{} 中可以随意命名,但是没有必要。通常还是使用和接口方法参数同名。

  • Mapper 接口中抽象方法的声明
Employee selectEmployee(Integer empId);
  • SQL语句
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
  select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}
</select>

传入实体类型参数

mapper 接口中的方法参数为实体类对象时,此时可以使用 ${}#{},通过访问实体类对象中的 属性名 获取属性值,注意${}需要手动加单引号
.
原理:Mybatis会根据#{}中传入的数据,加工成 getXxx()方法【pojo 类中必须有 get 方法】,通过反射在实体类对象中调用这个 get 方法,从而获取到对应的数据。填充到 #{} 解析后的问号占位符这个位置。

  • pojo
public class Employee {

    private Integer empId;

    private String empName;

    private Double empSalary;
    ... get|set
}
  • Mapper 接口中抽象方法的声明
int insertEmployee(Employee employee);
  • SQL语句
<insert id="insertEmployee">
  insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})
</insert>

传入多个简单类型参数

方案1:注解指定 @Param 注解 指定多个简单参数的 key。 key = @Param("value值") 可以理解成别名 [推荐]
.
方案2:mybatis 默认机制

  • arg0 arg1… 形参从左到右依次对应 (name, salary) 【name-> key = arg0】 【salary -> key = arg1
  • param1 param2… (name, salary) 【name-> key = param1】 【 salary -> key = param2
  • Mapper 接口中抽象方法的声明
int updateEmployee(@Param("empId") Integer empId,@Param("empSalary") Double empSalary);
  • SQL 语句
<update id="updateEmployee">
  update t_emp set emp_salary=#{empSalary} where emp_id=#{empId}
</update>

传入 Map 类型参数

有很多零散的参数需要传递,但是没有对应的实体类类型可以使用。使用@Param 注解一个一个传入又太麻烦了。所以都封装到Map中。
.
使用 map:手动创建map集合,将这些数据放在map中,只需要通过${}#{}访问map集合的键就可以获取相对应的值,

  • Mapper 接口中抽象方法的声明
int updateEmployeeByMap(Map<String, Object> paramMap);
  • SQL语句
<update id="updateEmployeeByMap">

  update t_emp set emp_salary=#{empSalaryKey} where emp_id=#{empIdKey}

</update>
  • 测试类
private SqlSession session;
//junit5会在每一个@Test方法前执行@BeforeEach方法
@BeforeEach
public void init() throws IOException {
    session = new SqlSessionFactoryBuilder()
            .build(
                    Resources.getResourceAsStream("mybatis-config.xml"))
            .openSession();
}

@Test
public void testUpdateEmpNameByMap() {
  EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
  Map<String, Object> paramMap = new HashMap<>();
  //设置要传入的 map 的 key 和 value
  paramMap.put("empSalaryKey", 999.99);
  paramMap.put("empIdKey", 5);
  //把 map 传递进去
  int result = mapper.updateEmployeeByMap(paramMap);
  System.out.println("result = " + result);
}

//junit5会在每一个@Test方法后执行@@AfterEach方法
@AfterEach
public void clear() {
    session.commit();
    session.close();
}

@Param 取别名

在 MyBatis 中,@Param 是一个注解,它主要用于在 Mapper 接口的方法参数上指定参数名。

  • UserMapper 接口
public interface UserMapper {

    /**
     * 验证登录 (@Param注解)
     * @param username
     * @param password
     * @return
     */
    User checkLoginByParam(@Param("username") String username, @Param("password") String password);

}
  • UserMapper.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="com.atguigu.mybatis.mapper.UserMapper">
    <!--User checkLoginByParam(@Param("username") String username, @Param("password") String password);-->
    <!--
        这种情况会以@Param的值为 key, value 就是 后面的参数
        或者可以以 param1, param2 为 key
    -->
    <select id="checkLoginByParam" resultType="User">
        select * from t_user where username = #{param1} and password = #{param2};
    </select>
</mapper>

SQL 语句返回参数

返回参数概述

数据输出总体上有两种形式:

  • 增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
  • 查询操作的查询结果
    .

我们需要做的是,指定查询的输出数据类型即可!
并且插入场景下,实现主键数据回显示!

返回单个简单类型

retultType = 类的全限定名 | 别名:声明返回的类型

  • Mapper 接口中的抽象方法
int selectEmpCount();
  • SQL 语句
//这里 resultType 本来返回的是 java.lang.Integer 主类可以简写为 int
<select id="selectEmpCount" resultType="int">
  select count(*) from t_emp
</select>

别名问题

  • 基本数据类型 int double... -> _int _double...
  • 包装数据类型 Integer Double... -> int(或integer) double...
  • 集合容器类型 Map LIst HashMap.. -> 小写即可 map list...
  • 自定义类:使用<typeAliases> 标签

返回实体类对象

查询:返回单个实体类型,要求列名和属性名一致。才能进行实体类的属性映射

  • 解决方法1:给字段属性起别名
  • 解决方法2:在配置文件配置驼峰映射 自动进行驼峰映射 emp_id -> empId
  • Mapper 接口的抽象方法
Employee selectEmployee(Integer empId);
  • SQL 语句
<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
<!-- resultType属性:指定封装查询结果的Java实体类的全类名 除非起别名 -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">

  <!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符 -->
  <!-- 给每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->
  select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{maomi}

</select>

返回 Map 类型

适用于SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中。能够封装成实体类类型的,就不使用Map类型。
列名就是 key , 查询出的列的结果就是 value

  • Mapper 接口的抽象方法
Map<String,Object> selectEmpNameAndMaxSalary();
  • SQL语句
<!-- Map<String,Object> selectEmpNameAndMaxSalary(); -->
<!-- 返回工资最高的员工的姓名和他的工资 -->
<select id="selectEmpNameAndMaxSalary" resultType="map">
  SELECT
    emp_name 员工姓名,
    emp_salary 员工工资,
    (SELECT AVG(emp_salary) FROM t_emp) 部门平均工资
  FROM t_emp WHERE emp_salary=(
    SELECT MAX(emp_salary) FROM t_emp
  )
</select>
  • 测试类
@Test
public void testQueryEmpNameAndSalary() {

  EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);

  Map<String, Object> resultMap = employeeMapper.selectEmpNameAndMaxSalary();

  Set<Map.Entry<String, Object>> entrySet = resultMap.entrySet();

  for (Map.Entry<String, Object> entry : entrySet) {

	//key:就是列名员工姓名, 员工工资, 部门平均工资
    String key = entry.getKey();

	//value: 就是查询出来的值 jerry, 999.9 659
    Object value = entry.getValue();

	/*
		部门平均工资=659.363333333
		员工姓名=jerry
		员工工资=999.99
	*/
    log.info(key + "=" + value);

  }
}

返回 List 类型

查询结果返回多个实体类对象,希望把多个实体类对象放在List 集合中返回。此时不需要任何特殊处理,resultType 不需要指定集合类型, 只需要指定泛型即可也就是 实体类的类型。每一条记录封装成一个对象。然后全部放进 List 集合

  • Mapper 接口中抽象方法
List<Employee> selectAll();
  • SQL 语句
<!-- List<Employee> selectAll(); -->
<select id="selectAll" resultType="com.atguigu.mybatis.entity.Employee">
  select emp_id empId,emp_name empName,emp_salary empSalary
  from t_emp
</select>
  • 测试
@Test
public void testSelectAll() {
  EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
  List<Employee> employeeList = employeeMapper.selectAll();
  for (Employee employee : employeeList) {
    System.out.println("employee = " + employee);
  }
}

返回自增主键

Mybatis是将自增主键的值设置到实体类对象中,而不是以 Mapper 接口方法返回值的形式返回。

  • useGeneratedKeys = true:告诉MyBatis在执行插入操作后,要尝试获取由数据库自动生成的键值(通常是主键)
  • keyProperty = "...":指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性
  • Mapper 接口中抽象方法
int insertEmployee(Employee employee);
  • SQL 语句
<!-- int insertEmployee(Employee employee); -->
<!-- useGeneratedKeys属性告诉MyBatis在执行插入操作后,要尝试获取由数据库自动生成的键值(通常是主键)-->
<!-- keyProperty属性可以指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性 -->
<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="empId">
  insert into t_emp(emp_name,emp_salary)
  values(#{empName},#{empSalary})
</insert>
  • 测试类
@Test
public void testSaveEmp() {
  EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
  Employee employee = new Employee();
  employee.setEmpName("john");
  employee.setEmpSalary(666.66);
  employeeMapper.insertEmployee(employee);

	//自增主键返回给了 empId
  System.out.println("employee.getEmpId() = " + employee.getEmpId());
}

插入非自增长型主键

对于不支持自增主键的数据库(比如 Oracle),或者当你需要使用字符串类型的主键(如UUID)时,你可以使用 MyBatisselectKey 元素。
.
在插入记录之前,selectKey 元素会先运行。它会生成一个主键值,并将其设置到你的对象属性中。然后,MyBatis 才会执行插入操作。

.
这种做法非常适合使用 UUID 作为主键的情况。当然,生成主键的方法不止一种,你也可以在 Java 代码中生成 UUID 并手动设置到对象的属性里。不过,根据你的具体需求和场景,选择最合适的生成方式是非常重要的。
.
简单来说,selectKey 就是先帮你生成一个主键,然后MyBatis再用这个主键去插入新记录。

  • <selectKey>:帮你生成一个主键,然后MyBatis再用这个主键去插入新记录。
  • order = "before" | "after">: sql语句是在插入语句之前还是之后执行
  • resultType = ...:返回值类型
  • keyProperty = ...:查询结果给哪个属性赋值
  • SQL 语句
   <!--
        期望, 非自增长大的主键, 交给 mybatis 帮助我们维护
    -->
    <insert id="insertTeacher">

        <!--插入之前, 先指定一段sql语句, 生成一个主键值
            order="before | after" sql语句是在插入语句之前还是之后执行
            resultType = 返回值类型
            keyProperty = 查询结果给哪个属性赋值
        -->
        <selectKey order="BEFORE" resultType="string" keyProperty="tId">
            <!-- 把 UUID 中 - 换成空字符-->
            select REPLACE(UUID(),'-','');
        </selectKey>


        INSERT INTO teacher (t_id, t_name)
            VALUES(#{tId}, #{tName});
    </insert>

实体类属性和数据库字段不对应解决方法总结

返回单个实体类型,要求列名和属性名一致。才能进行实体类的属性映射

  • 规则要求数据库表字段命名方式:单词_单词
  • 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名

实体类 和 数据库字段

tId 对应 t_id
tName 对应 t_Name

解决方法一

方案1:起别名 select t_id tId, t_name tName from teacher where t_id = #{tId}

<select id="queryById" resultType="teacher">
    select t_id tId, t_name tName from teacher where t_id = #{tId}
</select>

解决方法二

propertiesyaml 配置文件开启驼峰式映射

mybatis:
  configuration:
    map-underscore-to-camel-case: true # 开启驼峰映射

~~~xml
<select id="queryById" resultType="teacher">
    select * from teacher where t_id = #{tId}
</select>

解决方法三

使用自定义映射

<!--
     resultMap: 设置自定义的映射关系
     id: 唯一标识
     type: 处理映射关系的实体类的类型
     常用的标签:
         id: 处理主键和实体类中实现的映射关系
         result: 处理普通字段和实体类中属性的映射关系
         column: 设置映射关系中的字段名, 必须是SQL查询出的某个字段
         property: 设置映射关系中的属性的属性名, 必须是处理的实体类类型中的属性名
-->
<resultMap id="tMap" type="teacher">
     <id column="t_id" property="tId"/>
     <result column="t_name" property="tName"/>
</resultMap>

<select id="queryById" resultType="tMap">
   select * from teacher where t_id = #{tId}
</select>

MyBatis 多表映射

实体类设计方案

对一

例如一个订单对应一个顾客*
.
实体类设计:对一关系下,类中只要包含单个对方对象类型属性即可!

//顾客表
public class Customer {

  private Integer customerId;
  private String customerName;
 
}

//订单表
public class Order {

  private Integer orderId;
  private String orderName;
  private Customer customer;// 体现的是对一的关系: 包含单个对方类型对象

}  
对多

例如一个顾客对应多个订单
.
实体类设计:对多关系下,类中只要包含对方类型集合属性即可!

public class Customer {

  private Integer customerId;
  private String customerName;
  private List<Order> orderList;// 体现的是对多的关系,包含对方集合类型
}

public class Order {

  private Integer orderId;
  private String orderName;

}
总结
  • 对一,属性中包含对方对象
  • 对多,属性中包含对方对象集合

只有真实发生多表查询时,才需要设计和修改实体类,否则不提前设计和修改实体类!

无论多少张表联查,实体类设计都是两两考虑!【就是一次考虑两张表之间的关系就行】

  • 比如 第一张表和第二张表。第二张表和第三张表这样
    .

在查询映射的时候,只需要关注本次查询相关的属性!例如:查询订单和客户的关系。不要关注客户对订单的关系。

自定义映射 <resultMap>

<!--
     resultMap: 设置自定义的映射关系
     id: 唯一标识
     type: 处理映射关系的实体类的类型
     常用的标签:
         id: 处理主键和实体类中实现的映射关系
         result: 处理普通字段和实体类中属性的映射关系
         column: 设置映射关系中的字段名, 必须是SQL查询出的某个字段
         property: 设置映射关系中的属性的属性名, 必须是处理的实体类类型中的属性名
-->
<resultMap id="tMap" type="teacher">
     <id column="t_id" property="tId"/>
     <result column="t_name" property="tName"/>
</resultMap>

<select id="queryById" resultType="tMap">
   select * from teacher where t_id = #{tId}
</select>

对象属性赋值 <association> [ 对一 ]

    <!--自定义映射关系, 定义嵌套对象的映射关系-->
    <resultMap id="orderMap" type="order">
        <!--第一层属性 order 对象-->
        <!-- order 的主键 id 标签-->
        <id column="order_id" property="orderId" />
        <!--普通列-->
        <result column="order_name" property="orderName" />
        <result column="order_Id" property="orderId" />


		 就是实体类里面还有个对象属性
        <!--对象属性赋值
            property 表里对象属性名
            javaType 表里对象类型
        -->
        <association property="customer" javaType="Customer">
            <id column="customer_id" property="customerId" />
            <result column="customer_name" property="customerName" />
        </association>

    </resultMap>

在这里插入图片描述

集合属性赋值 <collection> [ 对多 ]


    <resultMap id="customerMap" type="customer">
        <id column="customer_id" property="customerId"/>
      	<result column="customer_name"  property="customerName"/>

        <!--<association property="" 对一的对象进行赋值-->
        <!--给集合属性赋值
            property 集合属性名
            ofType 集合的泛型类型
        -->
        <collection property="orderList" ofType="order">
            <id column="order_id" property="orderId" />
           	<result column="order_name" property="orderName" />
            <result column="customer_id" property="customerId" />
            //注意这里不要考虑对一设置的对象属性 Customer
        </collection>

    </resultMap>

在这里插入图片描述

对一映射示例

需求

根据ID查询订单,以及订单关联的用户的信息!

  • 实体类
//Customer
@Data
public class Customer {

    private Integer customerId;

    private String customerName;

}

//Order
@Data
public class Order {

    private Integer orderId;

    private String orderName;

    private Integer customerId;

    //一个订单 对应一个客户 对一
    private Customer customer;

}
  • Mapper 接口
public interface OrderMapper {

    //根据id查询订单信息和订单对应的客户
    Order queryById(Integer id);

}
  • mapper.xml
<!-- namespace = 接口的全限定名-->
<mapper namespace="com.atguigu.mapper.OrderMapper">

    <!--自定义映射关系, 定义嵌套对象的映射关系-->
    <resultMap id="orderMap" type="order">
        <!--第一层属性 order 对象-->
        <!-- order 的主键 id 标签-->
        <id column="order_id" property="orderId" />
        <!--普通列-->
        <result column="order_name" property="orderName" />
        <result column="order_Id" property="orderId" />

        <!--对象属性赋值
            property 表里对象属性名
            javaType 表里对象类型
        -->
        <association property="customer" javaType="Customer">
            <id column="customer_id" property="customerId" />
            <result column="customer_name" property="customerName" />
        </association>

    </resultMap>

    <!--Order queryOrderById(Integer id);-->
    <select id="queryById" resultMap="orderMap">
        SELECT * FROM t_order tor JOIN t_customer tur
        ON tor.customer_id = tur.customer_id
        WHERE tor.order_id = #{id};
    </select>
</mapper>
  • 测试
public class MybatisTest {

    private SqlSession session;

    @BeforeEach
    public void init() throws IOException {
        session = new SqlSessionFactoryBuilder()
                .build(
                        Resources.getResourceAsReader("mybatis-config.xml"))
                .openSession();

    }

    @Test
    public void testToOne() {
        //查询订单和对应的客户
        OrderMapper mapper = session.getMapper(OrderMapper.class);
        Order order = mapper.queryById(1);
        System.out.println(order);
        System.out.println(order.getCustomer());
    }

    @AfterEach
    public void clean() {
        session.close();
    }

}

对多映射示例

  • pojo
//Customer
@Data
public class Customer {

    private Integer customerId;

    private String customerName;

    //一个客户对应多个订单
    //对多: 装对方类型的集合即可
    private List<Order> orderList;

}

//Order
@Data
public class Order {

    private Integer orderId;

    private String orderName;

    private Integer customerId;

    //一个订单 对应一个客户 对一
    private Customer customer;

}
  • Mapper 接口
public interface CustomerMapper {

    //查询所有客户信息以及客户对应的订单信息
    List<Customer> queryList();

}
  • mapper.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">

<!-- namespace = 接口的全限定名-->
<mapper namespace="com.atguigu.mapper.CustomerMapper">

    <resultMap id="customerMap" type="customer">
        <id column="customer_id" property="customerId"/>
      	<result column="customer_name"  property="customerName"/>

        <!--<association property="" 对一的对象进行赋值-->
        <!--给集合属性赋值
            property 集合属性名
            ofType 集合的泛型类型
        -->
        <collection property="orderList" ofType="order">
            <id column="order_id" property="orderId" />
           	<result column="order_name" property="orderName" />
            <result column="customer_id" property="customerId" />

            <!-- customer 需不需要赋值, 不需要-->
        </collection>

    </resultMap>

    <select id="queryList" resultMap="customerMap">
        SELECT * FROM t_order tor
        JOIN t_customer tur
        ON tor.customer_id = tur.customer_id
    </select>
</mapper>

在这里插入图片描述

多表映射优化 <autoMappingBehavior>


MyBatis 提供了三种自动映射列到字段或属性的模式:

  • NONE:关闭自动映射功能。
  • PARTIAL 【默认】:仅自动映射未定义嵌套结果映射的字段。
  • FULL:可自动映射包括嵌套结果集在内的任何复杂结果。
    .

MyBatis 中,将 autoMappingBehavior 设为full后,进行多表关联查询并使用 resultMap 映射时,若 列名属性名 符合命名映射规则(二者相等,或开启驼峰映射 规则也适用),对应的 result 标签 便 可省 略,系统能自动完成映射。

  • 修改 yaml 或者 properties
mybatis:
  configuration:
    auto-mapping-behavior: full # 多表映射可以自动省略符合驼峰式的 result 标签内容
  • 修改 teacherMapper.xml
<resultMap id="teacherMap" type="teacher">
    <id property="tId" column="t_id" />
    <!-- 开启自动映射,并且开启驼峰式支持!可以省略 result!-->
<!--        <result property="tName" column="t_name" />-->
    <collection property="students" ofType="student" >
        <id property="sId" column="s_id" />
<!--            <result property="sName" column="s_name" />-->
    </collection>
</resultMap>

MyBatis 动态语句

if 和 where 标签 【多条件动态判断】


<if>

  • 通过 test 属性 对传入的参数做比较运算。
  • 当运算结果为 true 时,会将标签内的 sql 语句进行拼接;若结果为 false,则不拼接标签内部语句。
  • 对于大于和小于的比较符号,不推荐直接写,建议使用实体符号(如 &gt; 表示大于,&lt; 表示小于)。

.
<where>

  • 自动添加 / 去除 where 关键字:只要其内部有任何一个 if 条件满足,就会自动添加 where 关键字;若所有 if 条件都不满足,会自动去掉 where 关键字。
  • 自动去除多余逻辑关键字:能够自动去掉多余的 andor 关键字,以此避免因条件不满足等情况出现语法错误,确保生成的 sql 语句符合语法规范。
<!-- namespace = 接口的全限定名-->
<mapper namespace="com.atguigu.mapper.EmployeeMapper">

 <!--List<Employee> query(@Param("name") String name,@Param("salary") Double salary);

  	假如两个都满足 where emp_name = #{name} and emp_salary = #{salary}
	假如第一个满足 where emp_name = #{name}
	假如第一个满足第二个不满足 where and emp_salary = #{salary} 错误
	假如都不满足 where 错误
	所以需要多弄一个 where 标签。自动处理这些情况
	
 -->
    <select id="query" resultType="employee">
        select * from t_emp
            <where>
                <if test="name != null">
                    emp_name = #{name}
                </if>
                <if test="salary != null and salary &gt; 100">
                    and emp_salary = #{salary}
                </if>
            </where>
    </select>

</mapper>

set 标签【更新信息】


<set>

  • 自动去掉多余的,
  • 自动添加 set 关键字
  <!--根据员工 id 更新员工的数据, 我们要求 传入的 name 和 salary 不为 null 的才更新
    int update(Employee employee);

    全部满足: set emp_name = #{empNamee}, emp_salary = #{empSalary} 没问题
    第一个满足: set emp_name = #{empNamee}, 多了个,
    第二个满足: set emp_salary = #{empSalary} 没问题
    都不满足: set 语法错误
    所以需要 set 标签自动处理这些情况

    set:
        1.自动去掉多余的,
        2.自动添加set关键字
    -->
    <update id="update">
        update t_emp
            <set>
                <if test="empName != null">
                    emp_name = #{empName}
                </if>
                <if test="empSalary">
                    emp_salary = #{empSalary}
                </if>
            </set>
           where emp_id = #{empId}
    </update>

choose / when / otherwise 标签【多条件选一】


在多个分支条件中,仅执行一个。

  • 从上到下依次执行条件判断
  • 遇到的第一个满足条件的分支会被采纳
  • 被采纳分支后面的分支都将不被考虑
  • 如果所有的 when test 分支都不满足,那么就执行 otherwise 分支
<!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) -->
<select id="selectEmployeeByConditionByChoose" resultType="com.atguigu.mybatis.entity.Employee">
    select emp_id,emp_name,emp_salary from t_emp
    where
    <choose>
        <when test="empName != null">emp_name=#{empName}</when>
        <when test="empSalary &lt; 3000">emp_salary &lt; 3000</when>
        <otherwise>1=1</otherwise>
    </choose>
    
    <!--
     第一种情况:第一个when满足条件 where emp_name=?
     第二种情况:第二个when满足条件 where emp_salary < 3000
     第三种情况:两个when都不满足 where 1=1 执行了otherwise
     -->
</select>

foreach 标签【批量操作】


  • collection属性:要遍历的集合
  • item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象
  • separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符
  • open属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串
  • close属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串

foreach 标签中的 collection 属性注意事项

在接口里,若对于 List 类型 的参数没有使用 @Param 注解去指定一个具体名字,此时在 foreach 标签的 collection 属性中,默认能够用 “collection” 或者 “list” 来引用这个 List 集合,我们可以从异常信息中发现这一情况,比如出现如下异常信息:

Parameter 'empList' not found. Available parameters are [arg0, collection, list]

不过在实际开发过程中,为防止因这种比较隐晦的表达方式而产生误会,更建议大家使用 @Param 注解清晰明确地声明变量的名称,之后在 foreach 标签的 collection 属性 里,按照 @Param 注解所指定的名称去引用传入的参数。*

如果需要多次遍历 SQL 语句注意事项

需要设置 allowMultiQueries=true

 <dataSource type="POOLED">
                <!-- 建立数据库连接的具体信息 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                //这里设置 在 url 后面添加 allowMultiQueries=true
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis-example?allowMultiQueries=true"/>
                <property name="username" value="root"/>
                <property name="password" value="Ting123321"/>
            </dataSource>
  • Mapper 接口
	//根据 id 的批量查询
    List<Employee> queryBatch(@Param("ids") List<Integer> id);

    //根据 id 批量删除
    List<Employee> deleteBatch(@Param("ids") List<Integer> ids);

    //批量添加
    int insertBatch(@Param("list") List<Employee> employeeList);

    //批量修改
    int updateBatch(@Param("list") List<Employee> employeeList);

Mapper.xml

   	<!--根据 id 批量查询-->
	<select id="queryBatch" resultType="Employee">
        select * from t_emp
                where emp_id in
               	<!-- id 就是 从 ids 集合中拿值, ids 表示 list 集合-->
        		<!--(1,2,3)-->
                <foreach collection="ids" open="(" separator="," close=")" item="id">
                    #{id}
                </foreach>
    </select>

    <!--根据id的批量删除-->
    <delete id="deleteBatch">
        delete from t_emp where id in
        				  <!--(1,2,3)-->
                          <foreach collection="ids" open="(" separator="," close=")" item="id">
                              #{id}
                          </foreach>
    </delete>


	<!--根据id的批量插入-->
    <insert id="insetBatch">
        insert into t_emp (emp_name, emp_salary)
                    values
        			<!--('xx', 200), ('xx', 200), ('xx', 200)-->
                    <foreach collection="list" separator="," item="employee">
                        (#{employee.empName}, #{employee.empSalary})
                    </foreach>
    </insert>

	<!--根据id的批量修改-->
    <!--如果一个标签涉及多个语句! 需要设置允许指定多语句【把sql语句遍历多遍】 需要设置allowMultQueries = true -->
    <update id="updateBatch">
        <foreach collection="list" item="emp">
            <!--
				update t_emp set emp_name = #{emp.empName}, emp_salary = #{emp.empSalary}
        		where emp_id = #{emp.empId};
					
				update t_emp set emp_name = #{emp.empName}, emp_salary = #{emp.empSalary}
        		where emp_id = #{emp.empId};
				
				...
				
			-->
            update t_emp set emp_name = #{emp.empName}, emp_salary = #{emp.empSalary}
            where emp_id = #{emp.empId};
        </foreach>
    </update>

SQL 片段抽取


<include reifd = "指定SQL片段">

   <sql id="selectSql">
        select * from t_emp
    </sql>

    <select id="query" resultType="employee">
       //这里就是抽取的SQL 等价于 select * from t_emp
        <include refid="selectSql" />
            <where>
                <if test="name != null">
                    emp_name = #{name}
                </if>
                <if test="salary != null and salary &gt; 100">
                    and emp_salary = #{salary}
                </if>
            </where>

    </select>

MyBatis 分页和逆向

SQL 分页回顾


查询数据

前面是索引,后面是查询几条数据

//从 0 所以开始查询 5 条数据
SELECT * FROM table LIMIT 0, 5

返回某页数据

  • 公式:limit ( (startPage-1) * pageSize, pageSize )
  • startPage要查询的页码pageSize此页面总记录数
比如
0     4     8

1     5     9

2     6     10

3     7      11

这里返回 第二页数据 就是 
SELECT * FROM table LIMIT ( (2-1) * 4,  4)
也就是
SELECT * FROM table LIMIT (4,  4) 
刚好从索引 4 开始返回 4 条数据

分页插件 PageHelper


MyBatis 对插件进行了标准化的设计,并提供了一套可扩展的插件机制。插件可以在用于语句执行过程中进行拦截,并允许通过自定义处理程序来拦截和修改 SQL 语句、映射语句的结果等。

  • 导入依赖
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.11</version>
</dependency>
  • mybatis-config.xml 配置分页插件

其中,com.github.pagehelper.PageInterceptorPageHelper 插件的名称,dialect 属性用于指定数据库类型(支持多种数据库)MyBatis 其他插件也要这么安装

<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <property name="helperDialect" value="mysql"/>
    </plugin>
</plugins>
@Service
public class EmpServiceImpl implements EmpService {
	
    @Autowird
    private EmpMapper empMapper;
    
    /**
     * @param page 页码
     * @param pageSize 每页记录数
     */
    @Override
    public PageResult<Emp> page(Integer page, Integer pageSize) {
       //1. 设置分页参数 分页查询的结果封装到 Page 对象中 
       /**
       Page 是 ArrayList 的子类
       所以 Page 本质上是 List 类型 
       Page 封装了分页查询所有的信息包括总页数....
       **/
       PageHelper.startPage(page, pageSize);
        
       //2. 执行查询
       List<Emp> empList = empMapper.list(); 
        
       //3. 解析查询结果, 并封装 
       Page<Emp> p = (Page<Emp>)empList;
         
       //Total 是获取总记录数 Reuslt 是获取结果列表 
       return new PageREsult<Emp>(p.getTotal(), p.getResult);
        
    }
}

逆向工程


ORM,即对象 - 关系映射技术,能使数据库操作更贴合面向对象思维。它分半自动、全自动两类:

  • 半自动 ORM:程序员要手动写 SQL 语句或配置文件,以此关联实体类与数据表,还得自行把查询结果转为实体对象。映射关系常用 XML 文件、注解来指定,像 MyBatis 就属此类,使用它需掌握扎实的 SQL 与数据库知识。
  • 全自动 ORM:实体类与数据表自动映射,调用 API 操作数据库时,框架自动生成、执行 SQL 语句,还能把查询结果无缝转换成实体对象,且自动优化性能。像 Hibernate、Spring Data JPA、MyBatis - Plus 等都是,上手相对容易,无需过多钻研 SQL 语法。

逆向工程

MyBatis 的逆向工程堪称开发 “利器”,它能自动产出持久层代码与映射文件。依据数据库表结构及预设参数,迅速生成实体类、Mapper.xml 文件、Mapper 接口等,助力开发者快速搭建 DAO 层,无缝切入业务开发
.
实现逆向工程主要有两条路:借助 MyBatis Generator 插件,或是利用 Maven 插件。操作时,得给定数据库连接 URL、用户名、密码、目标表名、生成文件路径等配置参数。
.
注意:逆向工程只能生成单表crud的操作,多表查询依然需要我们自己编写!

使用 MyBatisX 插件


MyBatisX 是一个 MyBatis 的代码生成插件,可以通过简单的配置和操作快速生成 MyBatis Mapper、pojo 类和 Mapper.xml 文件。下面是使用 MyBatisX 插件实现逆向工程的步骤:

第一步:安装插件

IntelliJ IDEA 中打开插件市场,搜索 MyBatisX 并安装。

第二步:IDEA 连接数据库

在这里插入图片描述
在这里插入图片描述

第三步:使用逆向插件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第四步:查看生成结果

在这里插入图片描述

MyBaits 配置文件解释

# 配置数据源信息
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
    url: //127.0.0.1:3306/test
    username: root
    password: 123321

# 配置整合 MyBatis
mybatis:
  # 指定 xml 文件
  mapper-locations: mapper/*.xml
  # 给包里的类起别名: 别名就是类名小写 比如 User user
  type-aliases-package: com.example.pojo
  configuration:
    # 多表映射可以自动省略符合驼峰式的 result 标签内容
    auto-mapping-behavior: full
    # 开启驼峰映射
    map-underscore-to-camel-case: true

设置类别名细节

namespace 用不了别名

mybatis:
  # 给包里的类起别名: 别名就是类名小写 比如 User user
  type-aliases-package: com.example.pojo

在批量声明的情况下,要更改单个别名用 @Alias("别名")

@Alias("author"):public class Author{
...
}

驼峰映射

返回单个实体类的类型,要求列名和属性名一致。才能进行实体类映射
.

  • 规则要求数据库表字段命名方式:单词_单词
  • 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名

多表映射优化细节

MyBatis 提供了三种自动映射列到字段或属性的模式:

  • NONE:关闭自动映射功能。
  • PARTIAL 【默认】:仅自动映射未定义嵌套结果映射的字段。
  • FULL:可自动映射包括嵌套结果集在内的任何复杂结果。
    .

MyBatis 中,将 autoMappingBehavior 设为full后,进行多表关联查询并使用 resultMap 映射时,若 列名属性名 符合命名映射规则(二者相等,或开启驼峰映射 规则也适用),对应的 result 标签 便 可省 略,系统能自动完成映射。

# 配置整合 MyBatis
mybatis:
  configuration:
    # 多表映射可以自动省略符合驼峰式的 result 标签内容
    auto-mapping-behavior: full

<resultMap id="teacherMap" type="teacher">
    <id property="tId" column="t_id" />
    <!-- 开启自动映射,并且开启驼峰式支持!可以省略 result!-->
<!--        <result property="tName" column="t_name" />-->
    <collection property="students" ofType="student" >
        <id property="sId" column="s_id" />
<!--            <result property="sName" column="s_name" />-->
    </collection>
</resultMap>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/962202.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【游戏设计原理】93 - 节奏

与“序-破-急”类似的节奏概念广泛存在于全球不同文化和创意领域中。以下是一些常见的节奏框架和理论&#xff0c;它们与“序-破-急”在本质上有相似之处&#xff0c;但体现出不同的风格和应用&#xff1a; 1. 三幕式结构&#xff08;Three-Act Structure&#xff09; 来源&a…

蓝桥云客 三羊献瑞

三羊献瑞 题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 观察下面的加法算式&#xff1a; 祥 瑞 生 辉 三 羊 献 瑞 -------------------三 羊 生 瑞 气其中&#xff0c;相同的汉字代表相同的数字&#xff0c;…

OpenCV:二值化与自适应阈值

目录 简述 1. 什么是二值化 2. 二值化接口 2.1 参数说明​​​​​ 2.2 示例代码 2.3 运行结果 3. 自适应阈值 3.1 参数说明 3.2 示例代码 3.3 运行结果 4. 总结 4.1 二值化 4.2 自适应阈值 相关阅读 OpenCV&#xff1a;图像的腐蚀与膨胀-CSDN博客 简述 图像二值…

【memgpt】letta 课程6: 多agent编排

Lab 6: Multi-Agent Orchestration 多代理协作 letta 是作为一个服务存在的,app通过restful api 通信 多智能体之间如何协调与沟通? 相互发送消息共享内存块,让代理同步到不同的服务的内存块

Java---猜数字游戏

本篇文章所实现的是Java经典的猜数字游戏 , 运用简单代码来实现基本功能 目录 一.题目要求 二.游戏准备 三.代码实现 一.题目要求 随机生成一个1-100之间的整数(可以自己设置区间&#xff09;&#xff0c;提示用户猜测&#xff0c;猜大提示"猜大了"&#xff0c;…

STM32标准库移植RT-Thread nano

STM32标准库移植RT-Thread Nano 哔哩哔哩教程链接&#xff1a;STM32F1标准库移植RT_Thread Nano 移植前的准备 stm32标准库的裸机代码&#xff08;最好带有点灯和串口&#xff09;RT-Thread Nano Pack自己的开发板 移植前的说明 本人是在读学生&#xff0c;正在学习阶段&a…

使用Navicat Premium管理数据库时,如何关闭事务默认自动提交功能?

使用Navicat Premium管理数据库时&#xff0c;最糟心的事情莫过于事务默认自动提交&#xff0c;也就是你写完语句运行时&#xff0c;它自动执行commit提交至数据库&#xff0c;此时你就无法进行回滚操作。 建议您尝试取消勾选“选项”中的“自动开始事务”&#xff0c;点击“工…

AutoDL 云服务器:xfce4 远程桌面 终端乱码 + 谷歌浏览器

/usr/bin/google-chrome-stable --no-sandbox --proxy-server"127.0.0.1:7890" 打开新的PowerShell ssh -p 54521 rootconnect.yza1.seetacloud.com /opt/TurboVNC/bin/vncserver -kill :1 rm -rf /tmp/.X1* USERroot /opt/TurboVNC/bin/vncserver :1 -desktop …

《STL基础之vector、list、deque》

【vector、list、deque导读】vector、list、deque这三种序列式的容器&#xff0c;算是比较的基础容器&#xff0c;也是大家在日常开发中常用到的容器&#xff0c;因为底层用到的数据结构比较简单&#xff0c;笔者就将他们三者放到一起做下对比分析&#xff0c;介绍下基本用法&a…

JavaScript网页设计案例(任务管理器)

任务管理器 功能描述&#xff1a;用户可以添加任务、删除任务&#xff0c;并且任务列表在页面刷新后不会丢失&#xff0c;还能进行任务过滤与搜索。代码实现思路 HTML 结构&#xff1a;创建输入框用于输入任务、按钮用于添加任务&#xff0c;以及无序列表用于展示任务列表。CSS…

模型I/O功能之模型包装器

文章目录 模型包装器分类LLM模型包装器、聊天模型包装器 截至2023年7月&#xff0c;LangChain支持的大语言模型已经超过了50种&#xff0c;这其中包括了来自OpenAI、Meta、Google等顶尖科技公司的大语言模型&#xff0c;以及各类优秀的开源大语言模型。对于这些大语言模型&…

机器人抓取与操作经典规划算法(深蓝)——2

1 经典规划算法 位姿估计&#xff1a;&#xff08;1&#xff09;相机系位姿 &#xff08;2&#xff09;机器人系位姿 抓取位姿&#xff1a;&#xff08;1&#xff09;抓取位姿计算 &#xff08;2&#xff09;抓取评估和优化 路径规划&#xff1a;&#xff08;1&#xff09;笛卡…

开发环境搭建-4:WSL 配置 docker 运行环境

在 WSL 环境中构建&#xff1a;WSL2 (2.3.26.0) Oracle Linux 8.7 官方镜像 基本概念说明 容器技术 利用 Linux 系统的 文件系统&#xff08;UnionFS&#xff09;、命名空间&#xff08;namespace&#xff09;、权限管理&#xff08;cgroup&#xff09;&#xff0c;虚拟出一…

【2024年华为OD机试】(B卷,100分)- 热点网站统计(Java JS PythonC/C++)

一、问题描述 题目描述 企业路由器的统计页面需要动态统计公司访问最多的网页URL的Top N。设计一个算法&#xff0c;能够高效动态统计Top N的页面。 输入描述 每一行都是一个URL或一个数字&#xff1a; 如果是URL&#xff0c;代表一段时间内的网页访问。如果是数字N&#…

Git图形化工具【lazygit】

简要介绍一下偶然发现的Git图形化工具——「lazygit」 概述 Lazygit 是一个用 Go 语言编写的 Git 命令行界面&#xff08;TUI&#xff09;工具&#xff0c;它让 Git 操作变得更加直观和高效。 Github地址&#xff1a;https://github.com/jesseduffield/lazygit 主要特点 主要…

单细胞-第五节 多样本数据分析,打分R包AUCell

文件在单细胞\5_GC_py\1_single_cell\3.AUCell.Rmd 1.基因 rm(list = ls()) load("g.Rdata")2.AUCell https://www.ncbi.nlm.nih.gov/pmc/articles/PMC9897923 IF: NA NA NA用这个文章里的方法,将单细胞亚群的marker基因与ros相关基因取交集,用作AUCell的基因集…

单片机基础模块学习——超声波传感器

一、超声波原理 左边发射超声波信号&#xff0c;右边接收超声波信号 左边的芯片用来处理超声波发射信号&#xff0c;中间的芯片用来处理接收的超声波信号 二、超声波原理图 T——transmit 发送R——Recieve 接收 U18芯片对输入的N_A1信号进行放大&#xff0c;然后输入给超声…

BWM 世界模型

DGX AGX Ominiverse With Cosmos 功能 1w 张 H100 训练了 3个月 使用 Ray 串流 数据 数据准备 处理 pipeline 数组组成 真实世界的物理数据 训练 1、使用 L1 损失&#xff0c;最小化 输入和重构视频之间的像素级差异 以及基于 VGG19 的一个特征感知损失 2、使用光流的损…

【深度分析】DeepSeek大模型技术解析:从架构到应用的全面探索

深度与创新&#xff1a;AI领域的革新者 DeepSeek&#xff0c;这个由幻方量化创立的人工智能公司推出的一系列AI模型&#xff0c;不仅在技术架构上展现出了前所未有的突破&#xff0c;更在应用领域中开启了无限可能的大门。从其混合专家架构&#xff08;MoE&#xff09;到多头潜…