Web应用技术(第十三周/第二次练习/7h)

news2024/11/18 18:40:36

本次练习基于how2java和课本,学习MyBatis高级映和分页

  • 1.MyBatis高级映射:
    • 1.1 相关XML标签及其属性:
      • (1)mapper标签:
      • (2)DOCTYPE标签:
      • (3)typeAliases标签:
      • (4)mappers标签:
      • (5)resultMap标签:
      • (6)association标签:
      • (7)collection:
    • 1.2 多对多:
      • (1)先知:
      • (2)实体类文件:
      • (3)xml配置文件:
      • (4)xml映射文件:
      • (5)测试类:
      • (6)运行查看效果:
    • 1.3 动态SQL:
      • (1)概述:
      • (2)先知
        • OGNL表达式:
      • (2)以foreach为例进行展示:
        • 映射文件——Product.xml:
        • 测试类——TestMybatis.java:
        • 运行效果:
  • 2.分页:

1.MyBatis高级映射:

MyBatis的高级映射是指在MyBatis中使用更加灵活和强大的技术来实现数据持久化和对象关系映射的方式。高级映射包括以下几种技术:

  1. 动态SQL:可以根据不同的条件动态生成SQL语句。

  2. 嵌套查询:可以在一个查询语句中嵌套另一个查询语句,实现复杂的查询操作。

  3. 延迟加载:可以在需要时才去加载关联对象,减少数据库的访问次数,提高性能。

  4. 自动映射:可以根据数据库表和Java类的命名规则自动映射属性和字段。

  5. 缓存:可以将查询结果缓存到内存中,提高查询性能。

  6. 批量操作:可以将多个操作一起提交到数据库,提高数据库操作效率。

这些高级映射技术可以让开发人员更加方便地使用MyBatis来实现数据持久化和对象关系映射,提高开发效率和系统性能。

1.1 相关XML标签及其属性:

(1)mapper标签:

MyBatis中的mapper标签是用来定义SQL语句和映射关系的,它可以将SQL语句和Java方法绑定起来,实现数据的持久化和对象关系映射。在使用MyBatis时,通常会将mapper标签定义在XML文件中,然后通过SqlSessionFactory加载和解析XML文件,创建SqlSession对象进行数据库操作。

具体来说,mapper标签包含了一组SQL语句和映射关系,可以通过namespace属性来指定命名空间,通过id属性来指定SQL语句的唯一标识符。在mapper标签内部,可以使用select、insert、update、delete等标签来定义具体的SQL语句和参数映射关系。例如:

<mapper namespace="com.example.UserMapper">
  <select id="getUserById" parameterType="int" resultType="com.example.User">
    SELECT * FROM user WHERE id = #{id}
  </select>
  <insert id="insertUser" parameterType="com.example.User" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user(name, age) VALUES(#{name}, #{age})
  </insert>
  <update id="updateUser" parameterType="com.example.User">
    UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}
  </update>
  <delete id="deleteUser" parameterType="int">
    DELETE FROM user WHERE id = #{id}
  </delete>
</mapper>

上面的例子中,mapper标签定义了一个命名空间为com.example.UserMapper的mapper,包含了四个SQL语句和对应的参数映射关系。
其中,
select标签定义了一个id为getUserById的SQL语句,使用了一个int类型的参数id和一个com.example.User类型的返回值。
insert标签定义了一个id为insertUser的SQL语句,使用了一个com.example.User类型的参数,并且开启了自动生成主键的功能。
update标签定义了一个id为updateUser的SQL语句,使用了一个com.example.User类型的参数。
delete标签定义了一个id为deleteUser的SQL语句,使用了一个int类型的参数id。


(2)DOCTYPE标签:

MyBatis配置文件中的DOCTYPE声明,它指定了MyBatis配置文件的DTD(Document Type Definition),用于验证和解析XML文件的结构和内容。它的作用是告诉XML解析器使用指定的DTD来验证和解析XML文件,确保XML文件的结构和内容符合规范。如果XML文件的结构和内容不符合DTD的规范,解析器会报错并停止解析。它不是必须的,但建议在MyBatis配置文件中使用,以确保XML文件的正确性和可读性。

下面是一个示例MyBatis配置文件,包含了DOCTYPE声明:

<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="com/example/UserMapper.xml"/>
  </mappers>
</configuration>

上面的例子中,DOCTYPE声明指定了MyBatis 3.0版本的DTD,确保XML文件的结构和内容符合规范。configuration标签是MyBatis配置文件的根元素,包含了environments和mappers两个子元素,用于配置数据库环境和SQL映射文件。


(3)typeAliases标签:

MyBatis中的标签用于给Java类型起别名,方便在映射文件中使用。具体来说,<typeAliases>标签可以将Java类型的全限定名或简单类名映射为一个自定义的别名,然后在映射文件中使用别名代替Java类型的全限定名或简单类名,提高代码的可读性和可维护性。

标签有以下两个属性:

  • typeHandlerPackage:指定自动扫描的类型处理器所在的包名。
  • alias:指定Java类型的别名,格式为"别名=全限定名"或"别名=简单类名"。

标签可以包含以下两个子标签:

  • :用于指定自动扫描的Java类型所在的包名。可以使用*通配符指定多个包名,例如:。
  • :用于指定Java类型的别名和全限定名或简单类名。例如:。

下面是一个示例标签的用法:

<typeAliases>
  <package name="com.example.model"/>
  <typeAlias type="com.example.User" alias="User"/>
  <typeAlias type="com.example.Order" alias="Order"/>
</typeAliases>

上面的例子中,
<typeAliases>标签指定了自动扫描com.example.model包下的所有Java类型,
并将com.example.User和com.example.Order两个Java类型分别映射为User和Order两个别名。
这样,在映射文件中,我们就可以使用User和Order这两个别名代替Java类型的全限定名或简单类名,例如:

<select id="getUserById" parameterType="int" resultType="User">
  SELECT * FROM user WHERE id = #{id}
</select>

<insert id="insertOrder" parameterType="Order">
  INSERT INTO order(order_no, user_id) VALUES(#{orderNo}, #{userId})
</insert>

需要注意的是,

  • 使用标签自动映射Java类型时,别名的默认值是Java类型的简单类名,可以通过标签来指定自定义的别名。
  • 标签可以定义在MyBatis配置文件中,也可以定义在映射文件中。如果定义在MyBatis配置文件中,则可以在所有映射文件中使用别名;如果定义在映射文件中,则只能在当前映射文件中使用别名。

(4)mappers标签:

MyBatis中的<mappers>标签用于指定映射文件的位置或通过Java接口来声明映射关系。

具体来说,标签可以包含以下两个子标签:

  • mapper:用于指定映射文件的位置或通过Java接口来声明映射关系。可以使用resource、url、class三种方式来指定映射文件或Java接口,例如:<mapper resource="com/example/UserMapper.xml"/>
  • package:用于指定自动扫描的映射文件所在的包名。可以使用*通配符指定多个包名,例如:<package name="com.example.*"/>

标签还有以下两个属性:

  • enableLazyLoading:指定是否开启延迟加载,默认值为false。
  • defaultStatementTimeout:指定默认的SQL语句超时时间,单位为秒。

下面是一个示例标签的用法:

<mappers>
  <mapper resource="com/example/UserMapper.xml"/>
  <mapper class="com.example.OrderMapper"/>
  <package name="com.example.mapper"/>
</mappers>

上面的例子中,<mappers>标签指定了三种方式来声明映射关系:
通过resource属性指定了一个映射文件com/example/UserMapper.xml;
通过class属性指定了一个 Java接口com.example.OrderMapper;
通过package标签指定了一个自动扫描的包com.example.mapper。
这样,在MyBatis启动时,会自动加载并解析这些映射文件或Java接口,建立映射关系,以便在后续的数据库操作中使用。

需要注意的是,标签可以定义在MyBatis配置文件中,也可以定义在映射文件中。如果定义在MyBatis配置文件中,则可以在所有映射文件中使用;如果定义在映射文件中,则只能在当前映射文件中使用。


(5)resultMap标签:

MyBatis 中,resultMap 是一个映射结果集的标签,用于将 SQL 查询结果映射到 Java 对象中。resultMap标签可以定义在xml映射文件中,也可以定义在 Java 接口或抽象类中。

resultMap 标签有以下属性:

  • id:标识 resultMap 的唯一标识符,必须指定。
  • type:指定映射结果集的返回类型,可以是 Java 类型或别名,必须指定。
  • extends:指定继承的 resultMap 的 id,可选属性。

resultMap 标签有以下子标签:

  • id:用于映射主键字段。
  • result:用于映射普通字段。
  • association:用于关联另一个 resultMap。
  • collection:用于映射集合属性。

下面是一个 resultMap 的示例:

<resultMap id="userMap" type="User">
  <id column="id" property="id" />
  <result column="username" property="username" />
  <result column="password" property="password" />
  <result column="email" property="email" />
  <association property="profile" resultMap="profileMap" />
  <collection property="orders" ofType="Order">
    <id column="id" property="id" />
    <result column="order_no" property="orderNo" />
    <result column="total_price" property="totalPrice" />
  </collection>
</resultMap>

上面的例子中,
resultMap 标签的 id 属性为 “userMap”,type 属性为 “User”,表示将 SQL 查询结果映射到 User 类型的对象中。
其中,
id 子标签映射主键字段,
result 子标签映射普通字段,
association 子标签映射关联对象,
collection 子标签映射集合属性。其中,
association 子标签使用了另一个 resultMap,
即 “profileMap”,用于映射 User 类型中的 profile 属性。
collection 子标签的 ofType 属性指定集合元素的类型,即 Order 类型。

在使用 resultMap 时,可以在 SQL 映射语句中使用 resultMap 属性指定要使用的 resultMap,例如:

<select id="getUser" resultMap="userMap">
  SELECT * FROM user WHERE id = #{id}
</select>

(6)association标签:

MyBatis中,association是一种结果映射的方式,用于建立两个Java对象之间的多对一关系。当查询结果中包含了关联对象的信息时,可以使用association将关联对象映射到主对象的属性上。

association有以下常用属性:

  • property:指定主对象中的关联对象属性名。
  • javaType:指定关联对象的Java类型。
  • columnPrefix:指定关联对象在查询结果中的列名前缀。
  • resultMap:指定关联对象的结果映射。

举个例子,假设有两个Java对象Product和Category,它们之间是多对一关系,即一个商品属于一个分类,一个分类下有多个商品。那么可以定义如下的association:

<resultMap type="Product" id="productBean">
  <id column="pid" property="id" />
  <result column="pname" property="name" />
  <result column="price" property="price" />
  <association property="category" javaType="Category">
    <id column="cid" property="id"/>
    <result column="cname" property="name"/>
  </association>
</resultMap>

在上面的代码中,association定义了Product对象(java)中的category属性,类型为Category。
当查询结果中包含了Category对象的信息时,MyBatis会自动将Category对象映射到Product对象的category属性上。
其中,id和result分别定义了Category对象的主键和普通属性的映射关系。这样,在查询Product对象时,就可以同时获取到关联的Category对象的信息了。

简单来说,association用于声明外键。


(7)collection:

MyBatis中,collection是一种结果映射的方式,用于建立两个Java对象之间的一对多关系。当查询结果中包含了关联对象的集合信息时,可以使用collection将关联对象集合映射到主对象的属性上。

collection有以下常用属性:

  • property:指定主对象中的关联对象集合属性名。
  • ofType:指定关联对象集合中的元素类型。
  • columnPrefix:指定关联对象在查询结果中的列名前缀。
  • resultMap:指定关联对象的结果映射。

举个例子,假设有两个Java对象Category和Product,它们之间是一对多关系,即一个分类下有多个商品。那么可以定义如下的collection:

<resultMap type="Category" id="categoryBean">
  <id column="cid" property="id" />
  <result column="cname" property="name" />
  <collection property="products" ofType="Product">
    <id column="pid" property="id"/>
    <result column="pname" property="name"/>
    <result column="price" property="price"/>
  </collection>
</resultMap>

在上面的代码中,collection定义了Category对象中的products属性,类型为List。
当查询结果中包含了Category对象和其关联的Product对象集合时,
MyBatis会自动将Product对象集合映射到Category对象的products属性上。
其中,id和result分别定义了Product对象的主键和普通属性的映射关系。
这样,在查询Category对象时,就可以同时获取到其关联的Product对象集合信息了。


1.2 多对多:

(1)先知:

how2j上给定地是一个商品-商品种类-订单项-订单的模型
这里简单解释一下订单和订单项目:

  • 订单是指客户向商家提交的购买请求,包含了购买的商品、数量、价格、支付方式、收货地址等信息。一般来说,订单会被分配一个唯一的订单号,以便于商家和客户对订单进行跟踪和管理。

  • 订单项是指订单中的一个商品条目,包含了购买的商品、数量、价格等信息。一个订单可以包含多个订单项,每个订单项对应一个商品。因此,订单项是订单的组成部分,用于记录订单中每个商品的详细信息。

实现一对多,是通过collection标签实现的,
实现多对一,是通过association标签实现的,
那么,实现多对多就需要collection和association一起使用,更具体的说,是嵌套使用,即“嵌套结果映射”。

下方是我们希望在控制台呈现的内容与结构

code000A
	product a	88.879997	100
	product b	88.879997	101
	product c	88.879997	102

进一步抽象,得到

订单号—订单项—商品,
故我们需要配置的映射文件的结构应该如下图红色指示线所示

在这里插入图片描述

因此how2j上提供的映射配置文件中的内容是有很多无用部分的,这些部分是为前两节“一对多”和“多对一“服务的,比如说Category.xml的association标签及其包含内容,在多对多映射中,我们根本不需要product去关联category,因此这些无用标签删去后也可正常运行测试类,为了不增加工作量,我没有删除,但是测试了几个,确实是可以删除并正常运行的,这就说明我的理解没有出错。

在这里插入图片描述

文件目录结构如图所示:
在这里插入图片描述

(2)实体类文件:

Category.java:

package com.how2java.pojo;

import java.util.List;

public class Category {
	// 分类id
	private int id;
	// 分类名称
	private String name;
	// 分类下的商品列表
	List<Product> products;
	
	// 获取分类id的方法
	public int getId() {
		return id;
	}
	
	// 设置分类id的方法
	public void setId(int id) {
		this.id = id;
	}
	
	// 获取分类名称的方法
	public String getName() {
		return name;
	}
	
	// 设置分类名称的方法
	public void setName(String name) {
		this.name = name;
	}
	
	// 获取分类下的商品列表的方法
	public List<Product> getProducts() {
		return products;
	}
	
	// 设置分类下的商品列表的方法
	public void setProducts(List<Product> products) {
		this.products = products;
	}
	
	// 重写toString方法,用于打印分类信息
	@Override
	public String toString() {
		return "Category [id=" + id + ", name=" + name + "]";
	}
}

Order.java:
// 定义Order类
package com.how2java.pojo;

import java.util.List;

// Order类
public class Order {
	// 订单id
	private int id;
	// 订单编号
	private String code;
	
	// 订单项列表
	List<OrderItem> orderItems;
	
	// 获取订单id
	public int getId() {
		return id;
	}
	
	// 设置订单id
	public void setId(int id) {
		this.id = id;
	}
	
	// 获取订单编号
	public String getCode() {
		return code;
	}
	
	// 设置订单编号
	public void setCode(String code) {
		this.code = code;
	}
	
	// 获取订单项列表
	public List<OrderItem> getOrderItems() {
		return orderItems;
	}
	
	// 设置订单项列表
	public void setOrderItems(List<OrderItem> orderItems) {
		this.orderItems = orderItems;
	}

}

OrderItem.java:

// 定义OrderItem类
package com.how2java.pojo;

// OrderItem类
public class OrderItem {
	// 订单项id
	private int id;
	// 订单项数量
	private int number;
	// 订单
	private Order order;
	// 商品
	private Product product;
	
	// 获取订单项id
	public int getId() {
		return id;
	}
	
	// 设置订单项id
	public void setId(int id) {
		this.id = id;
	}
	
	// 获取订单项数量
	public int getNumber() {
		return number;
	}
	
	// 设置订单项数量
	public void setNumber(int number) {
		this.number = number;
	}
	
	// 获取订单
	public Order getOrder() {
		return order;
	}
	
	// 设置订单
	public void setOrder(Order order) {
		this.order = order;
	}
	
	// 获取商品
	public Product getProduct() {
		return product;
	}
	
	// 设置商品
	public void setProduct(Product product) {
		this.product = product;
	}
	
}

Product.java:

package com.how2java.pojo;

public class Product {
	private int id; // 产品的唯一标识符,用于识别不同的产品
	private String name; // 产品的名称,用于描述产品的特征
	private float price; // 产品的价格,用于表示产品的价值
	private Category category; // 产品所属的类别,用于分类管理产品

	public Category getCategory() { // 获取产品所属的类别
		return category;
	}

	public void setCategory(Category category) { // 设置产品所属的类别
		this.category = category;
	}

	public int getId() { // 获取产品的唯一标识符
		return id;
	}

	public void setId(int id) { // 设置产品的唯一标识符
		this.id = id;
	}

	public String getName() { // 获取产品的名称
		return name;
	}

	public void setName(String name) { // 设置产品的名称
		this.name = name;
	}

	public float getPrice() { // 获取产品的价格
		return price;
	}

	public void setPrice(float price) { // 设置产品的价格
		this.price = price;
	}

	@Override
	public String toString() { // 重写toString方法,用于打印产品信息
		return "Product [id=" + id + ", name=" + name + ", price=" + price + "]";
	}

}

(3)xml配置文件:

mybatis-config.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!-- 声明XML版本和编码 -->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--
MyBatis 配置文件的 DTD 声明,用于验证 XML 配置文件是否符合 MyBatis 的语法规范。
-->
<configuration>  
	<!-- 声明类型别名,用于简化 Java 类型的命名,方便在 XML 中引用。 -->
	<typeAliases>
	  <package name="com.how2java.pojo"/>
	</typeAliases>
    <!-- 声明环境,用于定义 MyBatis 的运行环境,包括事务管理器和数据源等。 -->
    <environments default="development">
        <environment id="development">
            <!-- 声明事务管理器类型,用于管理数据库事务,包括开启、提交、回滚等操作。 -->
            <transactionManager type="JDBC"/>
            <!-- 声明数据源类型,用于连接数据库,包括数据库驱动、URL、用户名和密码等信息。 -->
            <dataSource type="POOLED">
                <!-- 数据库驱动 -->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!-- 数据库连接URL -->
                <property name="url" value="jdbc:mysql://localhost:3306/how2java?characterEncoding=UTF-8"/>
                <!-- 数据库用户名 -->
                <property name="username" value="root"/>
                <!-- 数据库密码 -->
                <property name="password" value="admin"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 声明映射器,用于定义数据库操作的 SQL 语句和映射关系。 -->
    <mappers>
         <mapper resource="com/how2java/pojo/Product.xml"/>
        <mapper resource="com/how2java/pojo/Category.xml"/>
         <mapper resource="com/how2java/pojo/Order.xml"/>
        <mapper resource="com/how2java/pojo/OrderItem.xml"/>
        
    </mappers>
</configuration>

(4)xml映射文件:

Category.java:

<?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.how2java.pojo">
    <!-- 定义 resultMap,用于将查询结果映射到 Category 对象中 -->
    <resultMap type="Category" id="categoryBean">
        <!-- id 标签用于指定对象的主键属性,
        column 属性指定了查询结果中的列名,
        property 属性指定了对象中的属性名 -->
        <id column="cid" property="id" />
        <!-- result 标签用于指定对象的其他属性,
        column 和 property 属性的含义与 id 标签相同 -->
        <result column="cname" property="name" />
        
        <!-- 一对多的关系,
        collection 标签用于指定集合属性的名称和类型 -->
        <!-- property 属性指定了主对象集合属性的名称,
        	 ofType 属性指定了关联对象集合中元素的类型 -->
        <collection property="products" ofType="Product">
            <!-- id 标签用于指定集合元素的主键属性,
            column 和 property 属性的含义与 id 标签相同 -->
            <id column="pid" property="id" />
            <!-- result 标签用于指定集合元素的其他属性,
            column 和 property 属性的含义与 result 标签相同 -->
            <result column="pname" property="name" />
            <result column="price" property="price" />
        </collection>
    </resultMap>
    
    <!-- 关联查询分类和产品表 -->
    <!-- id 属性指定了 SQL 语句的唯一标识符 -->
    <!-- resultMap 属性指定了查询结果映射的 resultMap 的唯一标识符 -->
    <select id="listCategory" resultMap="categoryBean">
        <!-- 使用 left join 连接分类表和产品表 -->
        <!-- c.*, p.* 表示查询分类表和产品表的全部列 -->
        <!-- 别名语法用于将查询结果中的列映射到对象的属性中 -->
        <!-- column 属性指定了查询结果中的列名,'cid'、'pid'、'cname'、'pname' 是别名 -->
        <!-- property 属性指定了对象中的属性名 -->
        select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' 
        from category_ c
        left join product_ p on c.id = p.cid
    </select>   
		
		
			    <!-- 定义一个名为addCategory的插入操作,参数类型为Category -->
	    <insert id="addCategory" parameterType="Category" >
	        insert into category_ ( name ) values (#{name})    
	    </insert>
	    
	    <!-- 定义一个名为deleteCategory的删除操作,参数类型为Category -->
	    <delete id="deleteCategory" parameterType="Category" >
	        delete from category_ where id= #{id}   
	    </delete>
	    
	    <!-- 定义一个名为getCategory的查询操作,参数类型为int,结果类型为Category -->
	    <select id="getCategory" parameterType="_int" resultType="Category">
	        select * from   category_  where id= #{id}    
	    </select>

	    <!-- 定义一个名为updateCategory的更新操作,参数类型为Category -->
	    <update id="updateCategory" parameterType="Category" >
	        update category_ set name=#{name} where id=#{id}    
	    </update>
	    
	    <!-- 定义一个名为listCategory的查询操作,结果类型为Category -->
	    <select id="listCategory1" resultType="Category">
	        select * from   category_      
	    </select>	
	    
	    <!-- 定义一个名为listCategoryByName的查询操作,参数类型为string,结果类型为Category -->
	    <select id="listCategoryByName" parameterType="string" resultType="Category">
	        select * from   category_  where name like concat('%',#{0},'%')
	    </select>     
	            <select id="listCategoryByIdAndName"  parameterType="map" resultType="Category">
            select * from   category_  where id> #{id}  and name like concat('%',#{name},'%')
        </select>  
	</mapper>
	

Order.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文件,指定命名空间为com.how2java.pojo -->
<mapper namespace="com.how2java.pojo">
	
	<!-- 定义结果映射,type指定结果映射的Java类型,id指定结果映射的唯一标识 -->
	<resultMap type="Order" id="orderBean">
		
		<!-- 定义主键,column指定数据库表中的列名,property指定Java对象中的属性名 -->
		<id column="oid" property="id" />
		
		<!-- 定义普通属性,column指定数据库表中的列名,property指定Java对象中的属性名 -->
		<result column="code" property="code" />
		
		<!-- 定义集合属性,property指定Java主对象中的集合属性名,ofType指定关联对象集合元素的Java类型 -->
		<!-- 一对多 -->
		<collection property="orderItems" ofType="OrderItem">
			
			<!-- 定义集合元素的主键,column指定数据库表中的列名,property指定Java对象中的属性名 -->
			<id column="oiid" property="id" />
			
			<!-- 定义集合元素的普通属性,column指定数据库表中的列名,property指定Java对象中的属性名 -->
			<result column="number" property="number" />
			
			<!-- 定义集合元素的关联对象,property指定Java主对象中的对象属性名,javaType指定关联对象的Java类型 -->
		<!-- 多对1 -->
			<association property="product" javaType="Product">
				
				<!-- 定义关联对象的主键,column指定数据库表中的列名,property指定Java对象中的属性名 -->
				<id column="pid" property="id"/>
				
				<!-- 定义关联对象的普通属性,column指定数据库表中的列名,property指定Java对象中的属性名 -->
				<result column="pname" property="name"/>
				<result column="price" property="price"/>
			</association>				
		</collection>
	</resultMap>
	
	<!-- 定义查询语句,id指定查询语句的唯一标识,resultMap指定结果映射的唯一标识 -->
	<select id="listOrder" resultMap="orderBean">
		select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname' 
			from order_ o 
			left join order_item_ oi	on o.id =oi.oid 
			left join product_ p on p.id = oi.pid 
	</select>
	
	<!-- 定义查询语句,id指定查询语句的唯一标识,resultMap指定结果映射的唯一标识 -->
	<select id="getOrder" resultMap="orderBean">
		select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname' 
			from order_ o 
			left join order_item_ oi on o.id =oi.oid 
			left join product_ p on p.id = oi.pid 
		where o.id = #{id} 
	</select>
</mapper>

OrderItem.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文件,指定命名空间为com.how2java.pojo -->
<mapper namespace="com.how2java.pojo">
	
	<!-- 定义插入语句,id指定插入语句的唯一标识,parameterType指定传入参数的Java类型 -->
	<insert id="addOrderItem" parameterType="OrderItem">
		insert into order_item_ 
			values(null,#{order.id},#{product.id},#{number})
	</insert>    
	
	<!-- 定义删除语句,id指定删除语句的唯一标识,parameterType指定传入参数的Java类型 -->
	<insert id="deleteOrderItem" parameterType="OrderItem">
		delete from order_item_ 
			where oid = #{order.id} and pid = #{product.id}
	</insert>    
    
</mapper>

Product.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文件,指定命名空间为com.how2java.pojo -->
<mapper namespace="com.how2java.pojo">
	
	<!-- 定义结果映射,type指定结果映射的Java类型,id指定结果映射的唯一标识 -->
	<resultMap type="Product" id="productBean">
		
		<!-- 定义主键,column指定数据库表中的列名,property指定Java对象中的属性名 -->
		<id column="pid" property="id" />
		
		<!-- 定义普通属性,column指定数据库表中的列名,property指定Java对象中的属性名 -->
		<result column="pname" property="name" />
		<result column="price" property="price" />
	
		<!-- 定义多对一关系,property指定Java主对象中的关联对象属性名,javaType指定关联对象的Java类型 -->
		<association property="category" javaType="Category">
			
			<!-- 定义关联对象的主键,column指定数据库表中的列名,property指定Java对象中的属性名 -->
			<id column="cid" property="id"/>
			
			<!-- 定义关联对象的普通属性,column指定数据库表中的列名,property指定Java对象中的属性名 -->
			<result column="cname" property="name"/>
		</association>
	</resultMap>

	<!-- 定义查询语句,id指定查询语句的唯一标识,resultMap指定结果映射的唯一标识 -->
	<select id="listProduct" resultMap="productBean">
		select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' 
			from category_ c 
			left join product_ p on c.id = p.cid
	</select>    
	
	<!-- 定义查询语句,id指定查询语句的唯一标识,resultMap指定结果映射的唯一标识 -->
	<select id="getProduct" resultMap="productBean">
		select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' 
			from category_ c 
			left join product_ p on c.id = p.cid 
		where p.id = #{id}
	</select>    
	
</mapper>

(5)测试类:

TestMyBatis.java:

// 导入需要的类
package com.how2java;
 
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
 
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 com.how2java.pojo.Order;
import com.how2java.pojo.OrderItem;
import com.how2java.pojo.Product;
 
public class TestMybatis {
 
    public static void main(String[] args) throws IOException {
        // 加载MyBatis配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 打开一个新的SqlSession
        SqlSession session = sqlSessionFactory.openSession();
 
        // 调用listOrder方法
        listOrder(session);
 
        // 提交事务并关闭SqlSession
        session.commit();
        session.close();
 
    }
 
    private static void listOrder(SqlSession session) {
        // 调用selectList方法,查询所有订单
        List<Order> os = session.selectList("listOrder");
        for (Order o : os) {
            // 打印订单编号
            System.out.println(o.getCode());
            // 获取订单项列表
            List<OrderItem> ois= o.getOrderItems();
            for (OrderItem oi : ois) {
                // 打印订单项信息,包括商品名称、价格和数量
                System.out.format("\t%s\t%f\t%d%n", oi.getProduct().getName(),oi.getProduct().getPrice(),oi.getNumber());
            }
        }
    }
}

(6)运行查看效果:

运行TestMyBatis.java

在这里插入图片描述

理解多对多后,就可以很容易地理解一对多、多对一、一对一了,因为多对多 便是由它们构成的。

1.3 动态SQL:

(1)概述:

在 MyBatis 的高级映射中,动态 SQL 是一种根据不同条件生成不同 SQL 语句的技术。它可以根据条件判断循环动态生成 SQL 语句,从而使 SQL 语句更加灵活、可复用和易于维护。

动态 SQL 主要有以下几种形式:

  1. if 判断语句:根据条件判断是否添加 SQL 片段。

例如,在查询商品信息时,如果有商品名称的查询条件,则添加一个 WHERE 子句:

<select id="listProductByName" resultType="Product">
    SELECT * FROM product
    <where>
        <if test="name != null">
            AND name LIKE CONCAT('%', #{name}, '%')
        </if>
    </where>
</select>
  1. choose 语句:根据条件选择不同的 SQL 片段。

例如,在查询商品信息时,如果有商品名称和商品分类两个查询条件,可以使用 choose 语句根据条件选择不同的 SQL 片段:

<select id="listProduct" resultType="Product">
    SELECT * FROM product
    <where>
        <choose>
            <when test="name != null">
                AND name LIKE CONCAT('%', #{name}, '%')
            </when>
            <when test="category != null">
                AND category = #{category}
            </when>
            <otherwise>
                AND 1=1
            </otherwise>
        </choose>
    </where>
</select>
  1. foreach 循环语句:根据集合循环生成 SQL 片段。

例如,在批量插入商品信息时,可以使用 foreach 循环语句根据集合循环生成插入 SQL 语句:

<insert id="insertProducts" parameterType="List">
    INSERT INTO product(name, category, price)
    VALUES
    <foreach collection="list" item="product" separator=",">
        (#{product.name}, #{product.category}, #{product.price})
    </foreach>
</insert>

通过动态 SQL 技术,可以使 SQL 语句更加灵活、可复用和易于维护,从而提高开发效率和代码质量。


(2)先知

OGNL表达式:

OGNL(Object-Graph Navigation Language)是一种表达式语言,用于访问 Java 对象的属性、方法和索引等数据。它可以在 Java Web 开发中用于 JSP 页面、Struts2 框架、Spring框架等多个场景中,用于处理表单数据、访问数据库、控制页面显示等任务。

OGNL 表达式的语法类似于 Java 语言,支持访问对象属性、调用对象方法、访问数组和集合等操作。例如,以下是一些 OGNL 表达式的示例:

  • 访问对象属性:user.nameproduct.price 等。
  • 调用对象方法:user.getName()product.getPrice() 等。
  • 访问数组和集合:array[0]list[1] 等。

在 MyBatis 中,OGNL 表达式常用于映射文件中的SQL 语句中的条件判断、动态 SQL 语句的拼接等场景中。例如,在 MyBatis 映射文件中,可以使用 <if> 标签中的 test 属性指定一个 OGNL 表达式,用于判断条件是否成立,从而决定是否添加 SQL 语句的某些部分。例如:

<select id="listProduct" resultType="Product">
    SELECT * FROM product
    <where>
        <if test="name != null">
            AND name LIKE CONCAT('%', #{name}, '%')
        </if>
    </where>
</select>

在这个例子中,<if> 标签的 test 属性指定了一个 OGNL 表达式 name != null,用于判断传入的参数 name 是否为 null,从而决定是否添加 SQL 语句中的 where 子句。


(2)以foreach为例进行展示:

映射文件——Product.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义了DTD文件的位置和版本 -->
<!DOCTYPE mapper
	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义了命名空间 -->
<mapper namespace="com.how2java.pojo">
	<!-- 定义了查询语句,使用了ID作为查询语句的标识,resultType指定了查询结果的类型 -->
	<select id="listProduct" resultType="Product">
		<!-- 查询语句,查询了product_表中的所有列,并且只返回ID列的值在一个指定列表中的行 -->
		SELECT * FROM product_ 
		WHERE ID in
		<!-- 动态生成参数列表,参数列表就是指定列表中的元素 -->
		<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
			<!-- 每个元素都会被替换成#{item},其中item是一个变量名,用于引用列表中的每一个元素 -->
			#{item}
		</foreach>
	</select>	
</mapper>

测试类——TestMybatis.java:

package com.how2java;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

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 com.how2java.pojo.Product;

public class TestMybatis {

    public static void main(String[] args) throws IOException {
        // 加载 MyBatis 配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 构建 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 打开一个新的 SqlSession
        SqlSession session = sqlSessionFactory.openSession();

        // 构建一个 id 集合,用于查询指定 id 的产品
        List<Integer> ids = new ArrayList();
        ids.add(1);
        ids.add(3);
        ids.add(5);

        // 执行查询操作,返回查询结果
        List<Product> ps = session.selectList("listProduct",ids);
        // 遍历查询结果并输出
        for (Product p : ps) {
            System.out.println(p);
        }

        // 提交事务
        session.commit();
        // 关闭 SqlSession
        session.close();

    }
}


运行效果:

在这里插入图片描述

2.分页:

MyBatis中的分页是指将查询结果按照指定的大小分为多个部分,每次只查询其中的一部分,以减少查询的数据量和提高查询效率。实现分页的方式有多种,其中比较常用的方式是使用 SQL 语句中的 limit 关键字,通过设置 limit 子句的起始位置返回数据的数量来实现分页。

例如,下面的 SQL 语句将查询结果按照 id 排序,并返回第 10 条到第 20 条记录:

select * from my_table order by id limit 10, 10;

在 MyBatis 中,实现分页需要在映射文件中定义一个查询语句,并通过参数传递起始位置和返回数据的数量。例如,在上述代码中,可以通过添加以下代码来实现分页:

<select id="listProduct" parameterType="map" resultType="Product">
    select * from product
    where id in
    <foreach collection="ids" item="id" open="(" close=")" separator=",">
        #{id}
    </foreach>
    order by id
    limit #{start}, #{count}
</select>

其中,#{start}#{count} 分别表示起始位置返回数据的数量,可以通过参数传递。例如,在 Java 代码中,可以构造一个 Map 对象,将起始位置和返回数据的数量作为键值对存入其中,并作为参数传递给查询语句:

Map<String, Object> params = new HashMap<>();
params.put("ids", ids);
params.put("start", 0);
params.put("count", 10);
List<Product> ps = session.selectList("listProduct", params);

这样,就可以查询出 id 在指定集合中的商品列表的前 10 条记录。

对于第二份代码,如果要实现分页,只需要在查询语句中添加 limit 子句,并通过参数传递起始位置和返回数据的数量即可。例如,在查询所有分类的语句中,可以添加以下代码来实现分页:

<select id="listCategory" resultType="Category">
    select * from category_
    <if test="start != null and count != null">
        limit #{start}, #{count}
    </if>
</select>

然后,在 Java 代码中,可以构造一个 Map 对象,将起始位置和返回数据的数量作为键值对存入其中,并作为参数传递给查询语句:

Map<String, Object> params = new HashMap<>();
params.put("start", 0);
params.put("count", 10);
List<Category> cs = session.selectList("listCategory", params);

how2j分页章节运行截图:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL】MySQL体系架构

文章目录 背景一、MySQL体系架构二、网络连接层三、数据库服务层3.1 连接池3.2 系统管理和控制工具3.3 SQL接口3.4 解析树3.5 查询优化器3.6 缓存 四、存储引擎层五、系统文件层5.1 日志文件5.2 数据文件5.3 配置文件5.4 pid文件5.5 socket文件 背景 很多小伙伴工作很长时间了…

【OJ比赛日历】快周末了,不来一场比赛吗? #05.27-06.02 #14场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2023-05-27&#xff08;周六&#xff09; #8场比赛2023-05-28…

Holocube-第一集

准备工作&#xff1a; 装备&#xff1a; ESP 8266 D1 MINI1.3寸 TFT 屏幕分光棱镜25.4mm面包板、杜邦线、数据线等配件 软件&#xff1a; Arduino(本人使用1.8.16)---驱动ch341 初始arduino简单程序&#xff0c;对esp8266上传代码&#xff0c;能正常运行。正式开始 坑1&…

Day0:Windows编程环境搭建

前言&#xff1a;学习一个东西&#xff0c;首先要有三问&#xff1a; 1. 这个东西是什么&#xff1f; 2. 为什么要学它&#xff1f; 3. 该怎么学习它&#xff1f; 第一问是要明白你要学习的东西它是什么&#xff0c;你就会对它有一个整体的了解。这也是第二问的半个答案&#x…

有没有想过一种可能,30岁之后,转行去做IT售前?

灵魂拷问 IT行业的变化是非常迅速的&#xff0c;各种新技术、新产品、新观念、新的业务模式层出不穷&#xff0c;不仅是我们&#xff0c;客户也在不断地学习进步&#xff0c;因此我们注定要终身学习。 IT售前这个岗位为许多IT职场人提供了一种新的选择: 你不需要成为某一方面…

微信小程序推送消息

微信小程序推送消息 前言一、推送消息&#xff1a;小程序“订阅消息”功能二、开发步骤1.小程序调用wx.requestSubscribeMessage&#xff0c;进行消息订阅用户授权2.小程序调用wx.login&#xff0c;获取code3.后端访问请求&#xff0c;获取用户openId4.后端访问请求&#xff0c…

树形dp问题套路

⭐️前言⭐️ 本篇文章旨在将二叉树中的树形dp问题模板化&#xff0c;借助信息体传递的方式&#xff0c;通解树形dp问题。 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主将持续更新学习记录收获&#xff0c;友…

双目测距联合YOLOv8 项目总结

代码贴&#xff1a;双目测距--5 双目相机 联合 YOLOv8_爱钓鱼的歪猴的博客-CSDN博客 1、单目标定&#xff08;张友正标定法&#xff09; 获得左相机、右相机的cameraMatrix&#xff08;内部参数值&#xff09;、disCoeffs(畸变矩阵) 2、双目标定 固定左右相机的内部参数值、…

File类和IO流的相关面试(二)

一.IO流 1.什么是IO? I&#xff1a;Input&#xff0c;数据的读取&#xff0c;数据的输入 数据的输入&#xff0c;可以是从键盘输入&#xff0c;从文件读取&#xff0c;从网络接收... O&#xff1a;Output&#xff0c;写数据&#xff0c;数据的输出 数据的输出&#xff0c;可…

C++服务器框架开发2——头文件memory/typedef

该专栏记录了在学习一个开发项目的过程中遇到的疑惑和问题。 其教学视频见&#xff1a;[C高级教程]从零开始开发服务器框架(sylar) 上一篇&#xff1a;C服务器框架开发1——项目介绍/分布式/#ifndef与#pragma once C服务器框架开发2——头文件memory/typedef 目前进度memoryty…

C4D R26 渲染学习笔记 建模篇(1):参数模型

往期文章 介绍篇 C4D R26 渲染学习笔记&#xff08;1&#xff09;&#xff1a;C4D版本选择和初始UI框介绍 C4D R26 渲染学习笔记&#xff08;2&#xff09;&#xff1a;渲染流程介绍 C4D R26 渲染学习笔记&#xff08;3&#xff09;&#xff1a;物体基本操作快捷键 建模篇 …

JVM内存结构介绍

我们都知道&#xff0c;Java代码是要运行在虚拟机上的&#xff0c;而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域&#xff0c;这些区域都有各自的用途。其中有些区域随着虚拟机进程的启动而存在&#xff0c;而有些区域则依赖用户线程的启动和结束…

[Data structure]栈

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;数据结构。数据结构专栏主要是在讲解原理的基础上拿Java实现&#xff0c;有时候有C/C代码。 ⭐如果觉得文章写的…

一文学会随机森林在sklearn中的实现

1 概述 集成学习(ensemble learning)是时下非常流行的机器学习算法,它本身不是一个单独的机器学习算法,而是通 过在数据上构建多个模型,集成所有模型的建模结果。基本上所有的机器学习领域都可以看到集成学习的身影,在 现实中集成学习也有相当大的作用,它可以用来做市场…

8、URP自定义屏幕后处理

回到目录 大家好&#xff0c;我是阿赵。这次来说一下URP渲染管线里面怎样使用后处理效果&#xff0c;还有怎样去自定义后处理效果。 一、使用URP自带的后处理效果 要使用URP自带的后处理效果&#xff0c;方法很简单&#xff0c;和Unity内置渲染管线的PostProcessing后处理很…

深度学习实战——不同方式的模型部署(CNN、Yolo)

忆如完整项目/代码详见github&#xff1a;https://github.com/yiru1225&#xff08;转载标明出处 勿白嫖 star for projects thanks&#xff09; 目录 系列文章目录 一、实验综述 1.实验工具及及内容 2.实验数据 3.实验目标 4.实验步骤 二、ML/DL任务综述与模型部署知识…

python:容器:列表——常用操作

列表.append(元素)向列表中追加一个元素列表.extend(容器)将数据容器的内容依次取出&#xff0c;追加到列表尾部列表.insert(下标,元素)在指定下标处&#xff0c;插入指定的元素del 列表[下标]删除列表指定的下标元素列表.pop(下标)删除列表指定的下标元素列表.remove(元素)从前…

【LeetCode热题100】打卡第3天:无重复字符的最长子串

文章目录 无重复字符的最长子串⛅前言&#x1f512;题目&#x1f511;题解 无重复字符的最长子串 ⛅前言 大家好&#xff0c;我是知识汲取者&#xff0c;欢迎来到我的LeetCode热题100刷题专栏&#xff01; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&…

【高数+复变函数】傅里叶变换的性质

文章目录 【高数复变函数】傅里叶变换的性质一、常见性质1.1 线性性质1.2 位移性质1.3 微分性质1.4 积分性质1.5 乘积定理1.6 能量积分 二、卷积2.1 卷积运算2.2 运算应用2.3 卷积定理 三、相关函数 【高数复变函数】傅里叶变换的性质 上一节&#xff1a;【高数复变函数】傅里…

2.labelme转yolo格式和MS COCO格式

2.labelme转yolo格式和MS COCO格式 2.1 数据集划分 import os import random import shutildef moveimg(fileDir, tarDir):pathDir os.listdir(fileDir) # 取图片的原始路径filenumber len(pathDir)rate 0.01 # 自定义抽取图片的比例&#xff0c;比方说100张抽10张&…