本次练习基于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中使用更加灵活和强大的技术来实现数据持久化和对象关系映射的方式。高级映射包括以下几种技术:
动态SQL:可以根据不同的条件动态生成SQL语句。
嵌套查询:可以在一个查询语句中嵌套另一个查询语句,实现复杂的查询操作。
延迟加载:可以在需要时才去加载关联对象,减少数据库的访问次数,提高性能。
自动映射:可以根据数据库表和Java类的命名规则自动映射属性和字段。
缓存:可以将查询结果缓存到内存中,提高查询性能。
批量操作:可以将多个操作一起提交到数据库,提高数据库操作效率。
这些高级映射技术可以让开发人员更加方便地使用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 主要有以下几种形式:
- if 判断语句:根据条件判断是否添加 SQL 片段。
例如,在查询商品信息时,如果有商品名称的查询条件,则添加一个 WHERE 子句:
<select id="listProductByName" resultType="Product">
SELECT * FROM product
<where>
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
</where>
</select>
- 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>
- 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.name
、product.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分页章节运行截图: