進階查詢
Mybatis作為一個ORM架構,也對sql的進階查詢作了支援,下面我來學習mybatis中的一對一,一對多, 多對多
案例說明
此案例中的業務關系是使用者,訂單,訂單詳情,商品之間的關系,其中
一個訂單屬性于一個人
一個訂單可以有多個訂單詳情
一個訂單詳情中包含一個商品資訊
它們的關系是:
訂單和人是一對一關系
訂單和訂單詳情是一對多的關系
訂單和商品是多對多的關系
表分析
導入課程資料中的資料庫及實體類
業務需求
一對一查詢: 查詢訂單,并且查詢出下單人資訊。
一對多查詢:查詢訂單,查詢出下單人資訊并且查詢出訂單詳情。
多對多查詢:查詢訂單,查詢出下單人資訊并且查詢出訂單詳情中的商品資料。
一對一查詢
需求:查詢訂單,并且查詢出下單人資訊
sql分析
-- 查詢訂單,并且查詢出下單人的資訊
SELECT
*
FROM
tb_user as u
left join tb_order AS o ON u.id = o.user_id
WHERE
o.order_number = 20200921001
代碼實作
@Data
public class Order {
private Integer id;
private Long userId;
private String orderNumber;
//添加User對象
private User user;
}
public interface UserMapper {
/**
* 一對一:查詢訂單,并且查詢出下單人資訊
* @param orderNumber
* @return
*/
public Order oneToOne(@Param("orderNumber") String orderNumber);
}
<!--一對一-->
<resultMap id="oneToOneResultMap" type="Order" autoMapping="true">
<!--order的id-->
<id property="id" column="id"/>
<!--一對一映射
association: 一對一映射
property: 表示子對象的屬性名
javaType: 指定子對象的類型
autoMapping: 完成子對象屬性自動映射
-->
<association property="user" javaType="User" autoMapping="true">
<!--user的id-->
<id property="id" column="id"/>
<result property="userName" column="user_name"/>
</association>
</resultMap>
<!--一對一:查詢訂單,并且查詢出下單人資訊-->
<select id="oneToOne" resultMap="oneToOneResultMap">
SELECT * FROM
tb_user as u left join tb_order as o on u.id = o.user_id
WHERE
o.order_number = #{orderNumber}
</select>
public class UserDaoTest {
private UserMapper userMapper;
private SqlSession sqlSession;
SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception {
//指定核心配置檔案的位置
String resource = "mybatis-config.xml";
//加載核心配置檔案
InputStream inputStream = Resources.getResourceAsStream(resource);
//建構sqlSessionFactory對象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//擷取SqlSession對象,SqlSession可以操作crud
sqlSession = sqlSessionFactory.openSession();
//動态代理
userMapper = sqlSession.getMapper(UserMapper.class);
}
//一對一查詢
@Test
public void oneToOne(){
Order order = this.userMapper.oneToOne("20200921001");
System.out.println(order);
}
}
一對多查詢
**一對多查詢:**查詢訂單,查詢出下單人資訊并且查詢出訂單詳情
sql分析
-- 查詢訂單,查詢出下單人資訊并且查詢出訂單詳情。
SELECT
o.id as o_id,
u.id as u_id,
d.id as d_id,
u.user_name,
o.order_number,
d.total_price
FROM
tb_order AS o
left join tb_user AS u ON o.user_id = u.id
left join tb_orderdetail as d on d.order_id = o.id
WHERE
o.order_number = 20200921001;
代碼實作
/**
* 訂單表
*/
@Data
public class Order {
private Integer id;
private Long userId;
private String orderNumber;
//一對一:添加User對象
private User user;
//一對多:添加Orderdetail
private List<Orderdetail> orderdetails;
}
/**
* 一對多:查詢訂單,查詢出下單人資訊并且查詢出訂單詳情
* @param orderNumber
* @return
*/
public Order oneToMany(@Param("orderNumber") String orderNumber);
<!--一對多-->
<resultMap id="oneToManyResultMap" type="Order" autoMapping="true">
<!--映射order本身-->
<id property="id" column="o_id"/>
<!--映射user子對象-->
<association property="user" javaType="User" autoMapping="true">
<id property="id" column="u_id"/>
</association>
<!--映射Orderdetail-->
<collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">
<id property="id" column="d_id"/>
</collection>
</resultMap>
<!--一對多:查詢訂單,查詢出下單人資訊并且查詢出訂單詳情-->
<select id="oneToMany" resultMap="oneToManyResultMap">
SELECT
o.id as o_id,
u.id as u_id,
d.id as d_id,
u.user_name,
o.order_number,
d.total_price
FROM
tb_order AS o
left join tb_user AS u ON o.user_id = u.id
left join tb_orderdetail as d on d.order_id = o.id
WHERE
o.order_number = #{orderNumber};
</select>
//一對多
@Test
public void oneToMany(){
Order order = this.userMapper.oneToMany("20200921001");
System.out.println(order);
}
多對多查詢
**多對多查詢:**查詢訂單,查詢出下單人資訊并且查詢出訂單詳情中的商品資料
定單和商品表 是多對多的對應關系
sql分析
-- 查詢訂單,查詢出下單人資訊并且查詢出訂單詳情中的商品資料。
SELECT
o.id as o_id,
u.id as u_id,
d.id as d_id,
i.id as i_id,
u.user_name,
o.order_number,
d.total_price,
i.item_name
FROM
tb_order AS o
left join tb_user AS u ON o.user_id = u.id
left join tb_orderdetail as d on d.order_id = o.id
left join tb_item as i on d.item_id = i.id
WHERE
o.order_number = 20200921001
代碼實作
@Data
public class Orderdetail {
private Integer id;
private Double totalPrice;
private Integer status;
/*商品資訊*/
private Item item;
}
/**
* 多對多查詢:查詢訂單,查詢出下單人資訊并且查詢出訂單詳情中的商品資料。
* @param orderNumber
* @return
*/
public Order manyToMany(@Param("orderNumber") String orderNumber);
<!--多對多-->
<resultMap id="manyToManyResultMap" type="Order" autoMapping="true">
<!--映射order本身-->
<id property="id" column="o_id"/>
<!--映射user子對象-->
<association property="user" javaType="User" autoMapping="true">
<id property="id" column="u_id"/>
</association>
<!--映射Orderdetail-->
<collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">
<id property="id" column="d_id"/>
<!--映射item子對象-->
<association property="item" javaType="Item" autoMapping="true">
<id property="id" column="i_id"/>
</association>
</collection>
</resultMap>
<!--多對多查詢:查詢訂單,查詢出下單人資訊并且查詢出訂單詳情中的商品資料。-->
<select id="manyToMany" resultMap="manyToManyResultMap">
SELECT
o.id as o_id,
u.id as u_id,
d.id as d_id,
i.id as i_id,
u.user_name,
o.order_number,
d.total_price,
i.item_name
FROM
tb_order AS o
left join tb_user AS u ON o.user_id = u.id
left join tb_orderdetail as d on d.order_id = o.id
left join tb_item as i on d.item_id = i.id
WHERE
o.order_number = #{orderNumber}
</select>
//多對多
@Test
public void manyToMany(){
Order order = this.userMapper.manyToMany("20200921001");
System.out.println(order);
}
ResultMap的繼承
回顧以上多表映射中resultMap映射中其實有 一對一,一對多,多對多中都有一對一對映射很重複的,每一次都需要寫,不好,其實我們可以把相同的一對一映射進行抽取,然後再繼承過來。
代碼實作
分頁插件
PageHelper分頁插件
Mybatis的plugin實作原理
添加依賴
<!--分頁-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
配置插件
<!--分頁插件-->
<plugins>
<!-- com.github.pagehelper為PageHelper類所在包名 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!--資料庫方言-->
<property name="dialect" value="mysql"/>
<!-- 設定為true時,使用RowBounds分頁會進行count查詢 -->
<property name="rowBoundsWithCount" value="true"/>
</plugin>
</plugins>
5.x後續方案
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
<property name="reasonable" value="true"/>
</plugin>
實作分頁
/**
* 分頁查詢
* @return
*/
public List<User> queryAll();
<!--分頁查詢-->
<select id="queryAll" resultType="User">
select * from tb_user
</select>
//分頁查詢
@Test
public void queryAll(){
//實作分頁 參數1:目前頁(從1開始) 參數2:顯示多少條
PageHelper.startPage(2, 2);
//你隻管查詢所有,如何分頁交給 PageHelper.startPage(2, 2);完成
List<User> users = this.userMapper.queryAll();
for (User user : users) {
System.out.println(user);
}
//擷取更多的分頁資訊
PageInfo<User> pageInfo = new PageInfo<User>(users);
System.out.println("目前頁:" + pageInfo.getPageNum());
System.out.println("目前頁顯示的條數:" + pageInfo.getPageSize());
System.out.println("總頁碼:" + pageInfo.getPages());
System.out.println("最後一頁:" + pageInfo.getLastPage());
System.out.println("分頁相關的資訊:" + pageInfo.getList());
System.out.println("分頁總條數:" + pageInfo.getTotal());
}
懶加載(延遲加載)
思考問題?
一對多:我們查詢使用者時,要不要把關聯的訂單查詢出來
多對一:我們查詢訂單時,要不要把關聯的使用者查詢出來
什麼是懶加載?
就是在需要它的時候才加載,不需要的話就不加載
使用場景
一對多,多對多 通常采用延遲加載
一對一,多對一 通常采用立即加載
配置懶加載
<settings>
<!-- lazyLoadingEnabled:延遲加載啟動,預設是false 相當于是否開啟延遲加載 -->
<setting name="lazyLoadingEnabled" value="true" />
<!--aggressiveLazyLoading:積極的懶加載,falsed話按需加載,預設是true -->
<setting name="aggressiveLazyLoading" value="false"></setting>
</settings>
一對多 collection延遲加載
一對多:我們查詢使用者時,要不要把關聯的訂單查詢出來
@Data
public class User {
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private Integer sex;
private Date birthday;
private Date created;
private Date updated;
/*在一方添加多方對象*/
private List<Order> orders;
}
public List<User> queryAllUser(Integer id);
public Order queryOrderById(Integer id);
<resultMap id="userByIdMap" type="User" autoMapping="true">
<!--一對多,延遲加載-->
<collection property="orders" column="id" select="cn.yanqi.mapper.UserMapper.queryOrderById" ofType="Order" autoMapping="true"/>
</resultMap>
<select id="queryAllUser" resultMap="userByIdMap">
select * from tb_user where id = #{id}
</select>
<select id="queryOrderById" resultType="Order">
select * from tb_order where id = #{id}
</select>
@Test
public void queryUserById(){
List<User> list = this.userMapper.queryAllUser(2);
// list.forEach(System.out::println); 需要時才會加載order
}
多對一 association 延遲加載
多對一:我們查詢訂單時,要不要把關聯的使用者查詢出來
@Data
public class Order {
private Integer id;
private Long userId;
private String orderNumber;
//一對一:添加User對象
private User user;
//一對多:添加Orderdetail
private List<Orderdetail> orderdetails;
}
public Order queryOrderById2(Integer id);
<resultMap id="OrderResultMap" type="Order" autoMapping="true">
<id column="id" property="id"/>
<!--多對一:延遲加載 -->
<association property="user" javaType="User" column="id" select="queryUserById" fetchType="lazy" autoMapping="true"/>
</resultMap>
<!--order查詢-->
<select id="queryOrderById2" resultMap="OrderResultMap">
select * from tb_order where id = #{id}
</select>
<!--user查詢-->
<select id="queryUserById" resultType="User">
select * from tb_user where id = #{id}
</select>
@Test
public void queryOrderById2(){
Order order = this.userMapper.queryOrderById2(2);
User user = order.getUser();//需要時才加載user
System.out.println(user);
}
逆向工程
我們之前都是根據資料庫中表的字段來完成實體類的書寫,再mapper接口和對應的sql映射檔案,這此正常的操作是不是很重複,其實這些東西可以自動生成。以下就是告拆大家如何快速實作CRUD操作:
mybatis-generator
添加插件
- pom.xml
<build>
<plugins>
<!-- mybatis代碼生成插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.16</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
配置檔案
- datasource.properties
db.driverClassName=com.mysql.jdbc.Driver
db.url=jdbc:mysql:///mybatis?characterEncoding=utf-8
db.username=root
db.password=root
- generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--導入屬性配置-->
<properties resource="datasource.properties"></properties>
<!-- 指定資料庫驅動的jdbc驅動jar包的位置 -->
<!--<classPathEntry location="${db.driverLocation}" />-->
<!-- context 是逆向工程的主要配置資訊 -->
<!-- id:起個名字 -->
<!-- targetRuntime:設定生成的檔案适用于那個 mybatis 版本 -->
<context id="default" targetRuntime="MyBatis3">
<!--optional,旨在建立class時,對注釋進行控制-->
<commentGenerator>
<property name="suppressDate" value="true" />
<!-- 是否去除自動生成的注釋 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--jdbc的資料庫連接配接-->
<jdbcConnection driverClass="${db.driverClassName}"
connectionURL="${db.url}"
userId="${db.username}"
password="${db.password}">
</jdbcConnection>
<!--非必須,類型處理器,在資料庫類型和java類型之間的轉換控制-->
<javaTypeResolver>
<!-- 預設情況下資料庫中的 decimal,bigInt 在 Java 對應是 sql 下的 BigDecimal 類 -->
<!-- 不是 double 和 long 類型 -->
<!-- 使用常用的基本類型代替 sql 包下的引用類型 -->
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetPackage:生成的實體類所在的包 -->
<!-- targetProject:生成的實體類所在的硬碟位置 -->
<javaModelGenerator targetPackage="cn.yanqi.pojo"
targetProject=".\src\main\java">
<!-- 是否允許子包 -->
<property name="enableSubPackages" value="false" />
<!-- 是否對modal添加構造函數 -->
<property name="constructorBased" value="true" />
<!-- 是否清理從資料庫中查詢出的字元串左右兩邊的空白字元 -->
<property name="trimStrings" value="true" />
<!-- 建立modal對象是否不可改變 即生成的modal對象不會有setter方法,隻有構造方法 -->
<property name="immutable" value="false" />
</javaModelGenerator>
<!-- targetPackage 和 targetProject:生成的 mapper 檔案的包和位置 -->
<sqlMapGenerator targetPackage="mappers"
targetProject=".\src\main\resources">
<!-- 針對資料庫的一個配置,是否把 schema 作為字包名 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage 和 targetProject:生成的 interface 檔案的包和位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="cn.yanqi.dao" targetProject=".\src\main\java">
<!-- 針對 oracle 資料庫的一個配置,是否把 schema 作為字包名 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<table tableName="tb_user" domainObjectName="User"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
<table tableName="tb_order" domainObjectName="Order"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
<table tableName="tb_orderdetail" domainObjectName="Orderdetail"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
<table tableName="tb_item" domainObjectName="Item"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
</context>
</generatorConfiguration>
IDEA插件代碼生成器
Easy Code的代碼生成器,從實體類到controller service dao sql都給生成好,很友善
IDEA連接配接資料庫
下載下傳插件
生成代碼
false">