天天看點

JAVAEE架構技術之9-myBatis進階查詢技術文檔

進階查詢

Mybatis作為一個ORM架構,也對sql的進階查詢作了支援,下面我來學習mybatis中的一對一,一對多, 多對多

案例說明

此案例中的業務關系是使用者,訂單,訂單詳情,商品之間的關系,其中
    一個訂單屬性于一個人
    一個訂單可以有多個訂單詳情
    一個訂單詳情中包含一個商品資訊
它們的關系是:
    訂單和人是一對一關系
    訂單和訂單詳情是一對多的關系
    訂單和商品是多對多的關系      

表分析

導入課程資料中的資料庫及實體類

JAVAEE架構技術之9-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分析

JAVAEE架構技術之9-myBatis進階查詢技術文檔
-- 查詢訂單,查詢出下單人資訊并且查詢出訂單詳情。
 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映射中其實有 一對一,一對多,多對多中都有一對一對映射很重複的,每一次都需要寫,不好,其實我們可以把相同的一對一映射進行抽取,然後再繼承過來。

代碼實作

JAVAEE架構技術之9-myBatis進階查詢技術文檔

分頁插件

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>      

配置檔案

JAVAEE架構技術之9-myBatis進階查詢技術文檔
  • 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連接配接資料庫

JAVAEE架構技術之9-myBatis進階查詢技術文檔

下載下傳插件

JAVAEE架構技術之9-myBatis進階查詢技術文檔

生成代碼

JAVAEE架構技術之9-myBatis進階查詢技術文檔

false">