SSM 学習ノート ----MyBatis-plus

SSM 学習ノート ----MyBatis-plus

マイバティスプラス

学習目標

1. MyBatisPlus に基づいた標準 Dao の追加、削除、変更、およびクエリ機能を完了します。
2. MyBatisPlus でのページングと条件付きクエリの構築をマスターします。
3. 主キー ID の生成戦略をマスターします
。 4. MyBatisPlus のコード ジェネレーターを理解します。

1 MyBatisPlusのエントリー事例と紹介

このセクションでは、MyBatisPlus の導入事例と導入事例を学びます。他のコースとは異なり、最初に概念を紹介し、その後導入事例を作成します。MyBatisPlus の学習に関しては、順序を調整しました。主な理由は、MyBatisPlus は主に MyBatis を簡略化したものであるためです。そのため、まずどこが簡略化されているかを理解してから、それが何であり、何をするのかを学びます。

1.1 入門ケース

  • MybatisPlus(略してMP) は、MyBatisフレームワークに基づいて開発され、開発の簡素化と効率の向上を目的として開発された拡張ツールです。

  • 開発方法

    • MyBatis使用状況に基づいてMyBatisPlus
    • Spring使用状況に基づいてMyBatisPlus
    • SpringBoot使用状況に基づいてMyBatisPlus

SpringBootSpring学習が完了しました。他のテクノロジーを統合するための開発環境をすぐに構築できます。使い方は非常に簡単です。MP学習のために、SpringBootそれをベースにした学習も構築します。

学習する前に、SpringBoot統合Mybatis開発プロセスを確認してみましょう。

  • SpringBootプロジェクトの作成
    ここに画像の説明を挿入します

  • 開始依存関係パッケージを自動的に追加するための構成で使用されているテクノロジを確認してください。
    ここに画像の説明を挿入します

  • dataSource関連するプロパティ (JDBCパラメータ)を設定します

ここに画像の説明を挿入します

  • データ層インターフェース・マッピング構成の定義

ここに画像の説明を挿入します

上記の実装手順を参照して、SpringBoot統合を迅速に実装できますMyBatisPlus。具体的な実装手順は次のとおりです。

ステップ 1: データベースとテーブルを作成する
create database if not exists mybatisplus_db character set utf8;
use mybatisplus_db;
CREATE TABLE user (
    id bigint(20) primary key auto_increment,
    name varchar(32) not null,
    password  varchar(32) not null,
    age int(3) not null ,
    tel varchar(32) not null
);
insert into user values(1,'Tom','tom',3,'18866668888');
insert into user values(2,'Jerry','jerry',4,'16688886666');
insert into user values(3,'Jock','123456',41,'18812345678');
insert into user values(4,'传智播客','itcast',15,'4006184000');
ステップ 2: SpringBoot プロジェクトを作成する

ここに画像の説明を挿入します

ステップ 3: 使用テクノロジーの構成を確認する

ここに画像の説明を挿入します

例証します:

  • MPシステムの組み込み構成が含まれていないため、参加を直接選択することはできず、手動で構成して追加するidea必要があります。pom.xml
ステップ 4: pom.xml による依存関係の完了
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.1</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>

例証します:

  • druidデータ ソースは追加することも追加しないこともできます。組み込みのデータ ソースがあり、データ ソースSpringBootを使用するように構成できます。Druid

  • MP依存関係からわかるように、統合パッケージは依存関係の転送を通じてインポートされており、MyBatis 関連の jarMyBatisパッケージを追加する必要はありませんMyBatisSpringjar

ここに画像の説明を挿入します

ステップ 5: MP 関連の構成情報を追加する

resources構成ファイルはデフォルトで生成されます。これをファイルpropertiesに置き換えて、ファイル内にデータベース接続の関連情報を構成できます。ymlapplication.yml

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC 
    username: root
    password: root

例証します:serverTimezone はタイム ゾーンの設定に使用されます。UTC は標準タイム ゾーンであり、現在時刻とは 8 時間異なるため、次のように変更できます。Asia/Shanghai

ステップ 6: データベーステーブルに基づいてエンティティクラスを作成する
public class User {
    
       
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
    //setter...getter...toString方法略
}
ステップ 7: Dao インターフェースを作成する
@Mapper
public interface UserDao extends BaseMapper<User>{
    
    
}
ステップ 8: ブートストラップ クラスを作成する
@SpringBootApplication
//@MapperScan("com.itheima.dao")
public class Mybatisplus01QuickstartApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(Mybatisplus01QuickstartApplication.class, args);
    }

}

:Daoインターフェースをコンテナーによってスキャンしたい場合、解決策は 2 つあります。

  • 解決策 1:インターフェイスにアノテーションDaoを追加し、それがブート クラスが配置されているパッケージまたはそのサブパッケージ内にあることを 確認します。@MapperDao
    • このソリューションの欠点は、各 Dao インターフェイスに注釈を追加する必要があることです。
  • オプション 2: アノテーションをブート クラスに追加します。その属性は@MapperScanスキャン対象のDaoパッケージ です。
    • このソリューションの利点は、一度記述するだけで済み、指定されたパッケージ内のすべてのインターフェイスをDaoスキャンできるため、@Mapper記述する必要がないことです。
ステップ 9: テスト クラスを作成する
@SpringBootTest
class MpDemoApplicationTests {
    
    

	@Autowired
	private UserDao userDao;
	@Test
	public void testGetAll() {
    
    
		List<User> userList = userDao.selectList(null);
		System.out.println(userList);
	}
}

例証します:

userDao注入時に下に赤い線が出るのはなぜですか?

  • UserDaoこれはインターフェイスであり、オブジェクトをインスタンス化することはできません。

  • IOCサーバーがコンテナの初期化を開始した後でのみ、DAOフレームワークによって作成されたインターフェイスのプロキシ オブジェクトが挿入されます。

  • 現在、サーバーは起動されていないため、プロキシ オブジェクトは作成されておらず、IDEA対応するオブジェクト インジェクションが見つからないため、プロンプトは赤で表示されます。

  • サービスが開始されると、そのプロキシ オブジェクトを挿入できるため、このエラー メッセージは通常の動作には影響しません。

実行結果を表示します。
ここに画像の説明を挿入します

以前の統合と比較すると、インターフェイスにメソッドやステートメントを記述するMyBatis必要がなくなり、インターフェイスを継承するだけで済むことがわかります。全体的にかなり簡略化されています。DAOSQLBaseMapper

1.2 MybatisPlus の概要

MyBatisPlus(略して MP) は、MyBatisフレームワークに基づいて開発された拡張ツールであり、次のように設計されています。開発を簡素化し、効率を向上させる

今回の事例を通じて、開発の簡素化と効率化のメリットを皆さんも実感していただけると思います。

MyBatisPlus公式ウェブサイトは次のとおりです。https://mp.baomidou.com/

例証します:

ここに画像の説明を挿入します

現在のページでは、この行は削除されています。現在アクセスすると、https://mybatis.plusアクセスできないことがわかります。推測できる可能性がたくさんあるため、baomidouURL を使用してアクセスできます。

公式文書には、多くの友人がよく知っている写真があります。
ここに画像の説明を挿入します

この図から、の代替ではなく、 の最良のパートナーMPなることを目的としていることがわかります。つまり、強化ツールのセットとして理解できます。 をベース開発されています。つまり、にコンテンツを書き込むこともできます。MyBatisMyBatisMPMyBatisMyBatisMPMyBatisMPMyBatis

学習についてはMP、詳細なコード例が含まれている公式ドキュメントを参照してください。
MP特徴:

  • 非侵入的: 機能拡張のみが行われ、変更は行われません。既存のプロジェクトには影響しません。
  • 強力なCRUD操作: 組み込みのユニバーサルなMapper単一テーブルCRUD操作を少量の構成で実現できます。
  • サポートLambda: クエリ条件を記述するときに、間違ったフィールドを記述することを心配する必要はありません。
  • 主キーの自動生成をサポート
  • 組み込みのページングプラグイン
  • ……

2 標準データ層の開発

このセクションでは、データ層標準の実装とページング機能 (追加、削除、変更、確認) に焦点を当てますCRUDコードはたくさんあるので、一つずつ覚えていきましょう。

2.1 標準的な CRUD の使用法

標準CRUD機能にはどのようなものがありますか?また、MPその機能を使用するにはどのような方法がありますか?

まず写真を見てみましょう:
ここに画像の説明を挿入します

この図のメソッドについて、1 つずつ説明します。

まず、ケース内の環境は導入ケースの内容であり、最初は新增機能を完成させることです。

2.2 新規

新しいメソッドを追加する前に、新しいメソッドを分析できます。

int insert (T t)
  • T: 汎用、新しいデータを保存するために新しく追加されました

  • int: 戻り値。加算が成功した場合は 1 が返され、加算が成功しなかった場合は 0 が返されます。

テスト クラスで新しい操作を実行します。

@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
    
    

    @Autowired
    private UserDao userDao;

    @Test
    void testSave() {
    
    
        User user = new User();
        user.setName("黑马程序员");
        user.setPassword("itheima");
        user.setAge(12);
        user.setTel("4006184000");
        userDao.insert(user);
    }
}

テストの実行後、データがデータベース テーブルに追加されます。
ここに画像の説明を挿入します

しかし、データ内の主キー ID は少し長いです。では、この主キー ID はどこから来たのでしょうか? さらに必要なのは、主キーが自動的に増加することであり、これは 5 である必要があります。これが主キー ID 生成戦略です。この問題は今は脇に置いておきましょう。

2.3 削除

削除する前に、削除方法を分析できます。

int deleteById (Serializable id)
  • Serializable:パラメータの種類

    • 考察: なぜパラメータ型がシリアル化クラスなのでしょうか?

ここに画像の説明を挿入します

从这张图可以看出,
* String和Number是Serializable的子类,
* Number又是Float,Double,Integer等类的父类,
* 能作为主键的数据类型都已经是Serializable的子类,
* MP使用Serializable作为参数类型,就好比我们可以用Object接收任何数据类型一样。
  • int: 戻り値の型。データの削除に成功した場合は 1 が返され、データの削除に失敗した場合は 0 が返されます。

テスト クラスで新しい操作を実行します。

 @SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
    
    

    @Autowired
    private UserDao userDao;

    @Test
    void testDelete() {
    
    
        userDao.deleteById(1401856123725713409L);
    }
}

2.4 修正

変更を行う前に、変更方法を分析できます。

int updateById(T t);
  • T: 汎用、変更が必要なデータ内容変更は ID に基づいて行われるため、渡されたオブジェクトには ID 属性値が必要であることに注意してください。

  • int: 戻り値。変更が成功すると 1 が返され、データが変更されなかった場合は 0 が返されます。

テスト クラスで新しい操作を実行します。

@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
    
    

    @Autowired
    private UserDao userDao;

    @Test
    void testUpdate() {
    
    
        User user = new User();
        user.setId(1L);
        user.setName("Tom888");
        user.setPassword("tom888");
        userDao.updateById(user);
    }
}

: 変更する場合、エンティティ オブジェクト内の値を持つフィールドのみが変更されます。

2.5 IDに基づくクエリ

ID に基づいてクエリを実行する前に、ID に基づいてクエリを実行する方法を分析できます。

T selectById (Serializable id)
  • Serializable: パラメータの種類、主キーIDの値
  • T:ID に基づいてクエリを実行すると、1 つのデータのみが返されます。

テスト クラスで新しい操作を実行します。

@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetById() {
    
    
        User user = userDao.selectById(2L);
        System.out.println(user);
    }
}

2.6 すべてをクエリする

すべてをクエリする前に、すべてをクエリするメソッドを分析できます。

List<T> selectList(Wrapper<T> queryWrapper)
  • Wrapper: 条件付きクエリを構築するために使用される条件。現在、条件として直接渡すことができる条件はありません。Null
  • List<T>: クエリは all であるため、返されるデータはセットです

テスト クラスで新しい操作を実行します。

@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll() {
    
    
        List<User> userList = userDao.selectList(null);
        System.out.println(userList);
    }
}

私たちが呼び出すメソッドはすべて、DAOインターフェースを継承するBaseMapperクラスから来ています。そこには多くのメソッドが含まれており、後でゆっくりと内容を学びます。

2.7 ロンボク島

ここでコードを書いてみると、DAOインターフェースクラスを書くのが最も簡単になっており、何も書く必要がありません。次に、モデル クラスを作成するために何が必要かを見てみましょう。

  • 私有地
  • setter...getter...方法
  • toString方法
  • コンストラクタ

これらの内容は難しいものではなく、すべてIDEAツールを通じて生成されますが、プロセスはまだ完了する必要があります。モデル クラスを記述するための最適化方法はありますか? これは次に学習することになりますLombok

コンセプト
  • Lombokは、エンティティ クラスの開発をJava簡素化するための一連のアノテーションを提供するクラス ライブラリです。POJO
使用手順
ステップ 1: lombok 依存関係を追加する
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <!--<version>1.18.12</version>-->
</dependency>

SpringBoot: バージョンはすでに管理されているため、記述する必要はありませんlombok

ステップ 2: Lombok プラグインをインストールする

新しいバージョンIDEAにはプラグインが組み込まれており、削除プログラムsettergetterメソッド プログラムが赤で報告される場合は、プラグインをインストールする必要があります。
ここに画像の説明を挿入します

プラグインがIDEA見つからない場合は、次の Web サイトにアクセスしてください。lombok

https://plugins.jetbrains.com/plugin/6317-lombok/versions

ご使用のバージョンに応じて、IDEA対応するプラグインをダウンロードしlombok、ダウンロードが成功したら、IDEAオフライン インストール方法でインストールします。
ここに画像の説明を挿入します

ステップ 3: モデルクラスにアノテーションを追加する

Lombok一般的な注釈には次のものがあります。

  • @Setter:モデルクラスの属性のsetterメソッドを提供します
  • @Getter:モデルクラスの属性のgetterメソッドを提供します
  • @ToString:モデルクラスの属性のtoStringメソッドを提供します
  • @EqualsAndHashCode:モデルクラスの属性のメソッドequalshashcodeメソッドを提供します。
  • @Dataこれは、上記のアノテーションの機能を含む複合アノテーションです。
  • @NoArgsConstructorパラメーターなしのコンストラクターを提供する
  • @AllArgsConstructorすべてのパラメータを含むコンストラクタを提供します

Lombok他にも多くの注釈があります。上で赤でマークされた 3 つは、より一般的に使用されます。他のものは後で使用し、詳細を学習します。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    
    
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}

例証します:

Lombokこれは、モデル クラスの記述を単純化するだけです。以前の方法も使用できます。たとえば、誰かが「 と を含むコンストラクターだけが必要な場合、どのようにname記述passwordすればよいですか?」と尋ねるかもしれません。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    
    
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;

    public User(String name, String password) {
    
    
        this.name = name;
        this.password = password;
    }
}

このアプローチは許可されています。

2.8 ページング機能

基本的な追加、削除、変更、検索の学習はすでに終了しています 先ほど基礎開発を分析していたところ、まだ実装されていないページング機能がありました 次はページング機能をどのように実装するかを学ぶ必要がありMPます。

ページング クエリに使用されるメソッドは次のとおりです。

IPage<T> selectPage(IPage<T> page, Wrapper<T> queryWrapper)
  • IPage: ページング クエリ条件の構築に使用されます。
  • Wrapper: 条件付きクエリを構築するために使用される条件。現在、条件として直接渡すことができる条件はありません。Null
  • IPage: 戻り値。ページング条件とメソッドを構築する際の戻り値が次のとおりであることがわかります。IPage

IPageこれはインターフェースです。構築するにはその実装クラスを見つける必要があります。特定の実装クラスについては、クラスを入力して をIPageクリックするCtrl+Hと、実装クラスがあることがわかりますPage

ステップ 1: メソッドを呼び出し、パラメータを渡して戻り値を取得する
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    //分页查询
    @Test
    void testSelectPage(){
    
    
        //1 创建IPage分页对象,设置分页参数,1为当前页码,3为每页显示的记录数
        IPage<User> page=new Page<>(1,3);
        //2 执行分页查询
        userDao.selectPage(page,null);
        //3 获取分页结果
        System.out.println("当前页码值:"+page.getCurrent());
        System.out.println("每页显示数:"+page.getSize());
        System.out.println("一共多少页:"+page.getPages());
        System.out.println("一共多少条数据:"+page.getTotal());
        System.out.println("数据:"+page.getRecords());
    }
}
ステップ 2: ページング ブロッカーを設定する

このインターセプターは提供されているので、管理対象オブジェクトMPとして構成するだけで済みますSpringbean

@Configuration
public class MybatisPlusConfig {
    
    
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
    
    
        //1 创建MybatisPlusInterceptor拦截器对象
        MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
        //2 添加分页拦截器
        mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mpInterceptor;
    }
}

説明: 上記のコードを思い出せない場合はどうすればよいですか?

これらの内容はMPの公式ドキュメントに詳しく記載されており、公式ドキュメントのクラス構成を確認することができます。

ここに画像の説明を挿入します

ステップ 3: テスト プログラムを実行する

ここに画像の説明を挿入します
MP によって実行された SQL ステートメントを表示したい場合は、application.yml 構成ファイルを変更できます。

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印SQL日志到控制台

ログをオンにすると、対応する SQL ステートメントをコンソールに出力できます。ログ機能をオンにするとパフォーマンスに影響します。デバッグ後は忘れずにオフにしてください。
ここに画像の説明を挿入します

3 DQL プログラミング制御

追加、削除、変更、確認の 4 つの操作のうち、クエリは非常に重要かつ非常に複雑な操作です。この領域に焦点を当てる必要があります。このセクションで学習する主な内容は次のとおりです。

  • 条件付きクエリ方式
  • クエリプロジェクション
  • クエリ条件の設定
  • フィールドマッピングとテーブル名のマッピング

3.1 条件付きクエリ

3.1.1 条件付きクエリのクラス
  • MyBatisPlus複雑なSQLクエリ条件をカプセル化し、プログラミングを使用してクエリ条件の組み合わせを完成させます。

これは以前にも見たことがあります。たとえば、すべてのクエリやページング クエリを実行するときに、Wrapper次の図に示すように、クラスを使用してクエリ条件を構築します。
ここに画像の説明を挿入します

では、条件付きクエリを使用してWrapper構築するにはどうすればよいでしょうか?

3.1.2 環境構築

条件付きクエリを構築する前に、まず環境を準備しましょう

  • SpringBootプロジェクトを作成する

  • pom.xml対応する依存関係を追加します

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.5.0</version>
        </parent>
        <groupId>com.itheima</groupId>
        <artifactId>mybatisplus_02_dql</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
    
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.4.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.16</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    
  • 書き込みUserDaoインターフェース

    @Mapper
    public interface UserDao extends BaseMapper<User> {
          
          
    }
    
  • モデルクラスを書く

    @Data
    public class User {
          
          
        private Long id;
        private String name;
        private String password;
        private Integer age;
        private String tel;
    }
    
  • ブートクラスの書き込み

    @SpringBootApplication
    public class Mybatisplus02DqlApplication {
          
          
    
        public static void main(String[] args) {
          
          
            SpringApplication.run(Mybatisplus02DqlApplication.class, args);
        }
    
    }
    
  • 設定ファイルの書き込み

    # dataSource
    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
        username: root
        password: root
    # mp日志
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
  • テストクラスを書く

    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
          
          
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
          
          
            List<User> userList = userDao.selectList(null);
            System.out.println(userList);
        }
    }
    

    作成される最終的なプロジェクト構造は次のとおりです。
    ここに画像の説明を挿入します

  • テスト中、コンソールは大量のログを出力しましたが、これは少し遅く、実行結果の表示には役立たなかったので、次にこのログを処理します。

    • 初期化springログの印刷をキャンセルし、resourcesディレクトリに追加しますlogback.xml。名前は固定で、内容は次のとおりです。

      <?xml version="1.0" encoding="UTF-8"?>
      <configuration>
      </configuration>
      

      logback.xml設定内容は調査の対象ではありませんので、興味があればBaiduで確認してください。

    • MybatisPlus起動をキャンセルbannerアイコン
      ここに画像の説明を挿入します

      application.yml以下を追加します。

      # mybatis-plus日志控制台输出
      mybatis-plus:
        configuration:
          log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        global-config:
          banner: off # 关闭mybatisplus启动图标
      
    • 印刷をキャンセルしましたSpringBoot_log
      ここに画像の説明を挿入します

      application.yml以下を追加します。

      spring:
        main:
          banner-mode: off # 关闭SpringBoot启动图标(banner)
      

過剰なコンソール印刷ログの問題を解決するために関連する操作を行う必要はありませんが、通常、コンソール印刷ログはプログラムの動作結果を簡単に確認できるようにするために使用されます。

3.1.3 条件付きクエリの構築

クエリを作成するとき、エントリ ポイントはWrapperこのクラスにあります。これはインターフェイスであるため、対応する実装クラスを見つける必要があります。多くの実装クラスがあり、クエリ条件オブジェクトを構築する複数の方法があることを示しています。

ここに画像の説明を挿入します

  1. まず最初のものを見てみましょう:QueryWrapper
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        QueryWrapper qw = new QueryWrapper();
        qw.lt("age",18);
        List<User> userList = userDao.selectList(qw);
        System.out.println(userList);
    }
}
  • lt: より小さい (<)、最後の SQL ステートメントは次のとおりです。

    SELECT id,name,password,age,tel FROM user WHERE (age < ?)
    

最初の方法を導入した後は、条件の記述時に間違いが発生しやすく、たとえば年齢を間違って記述するとクエリが失敗するという小さな問題があります。

  1. 2 番目のタイプを見てみましょう。QueryWrapper以下に基づいて使用します。lambda
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        QueryWrapper<User> qw = new QueryWrapper<User>();
        qw.lambda().lt(User::getAge, 10);//添加条件
        List<User> userList = userDao.selectList(qw);
        System.out.println(userList);
    }
}
  • User::getAget式内のlambdaクラス名とメソッド名で、最後のステートメントは次のようになります。::sql
SELECT id,name,password,age,tel FROM user WHERE (age < ?)

:LambdaQueryWrapperビルド時にジェネリックを省略することはできません。

このとき条件を書き直すと間違った名前は書きませんが、qw後でlambda()呼び出しのレイヤーが追加されます。

  1. 3 番目のタイプを見てみましょう。LambdaQueryWrapper
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.lt(User::getAge, 10);
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
    }
}

この方法は、前の方法の問題を解決します。

3.1.4 複数条件の構築

クエリ オブジェクトの構築方法を 3 つ学びました。それぞれに特徴があるので、どれを使っても大丈夫です。先ほどは条件でしたが、複数の条件がある場合はどのように構築すればよいでしょうか?

要件: データベース テーブル内の 10 歳から 30 歳までのユーザー情報をクエリする

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.lt(User::getAge, 30);
        lqw.gt(User::getAge, 10);
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
    }
}
  • gt: (>) より大きい、最後のSQLステートメントは次のとおりです。

    SELECT id,name,password,age,tel FROM user WHERE (age < ? AND age > ?)
    
  • 複数の条件を構築する場合、チェーンプログラミングをサポートできます

    LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
    lqw.lt(User::getAge, 30).gt(User::getAge, 10);
    List<User> userList = userDao.selectList(lqw);
    System.out.println(userList);
    

要件: データベース テーブル内のデータの年齢が 10 未満または 30 より古いデータをクエリする

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
    }
}
  • or()sqlこれはステートメント内のキーワードと同等でありor、default がなければand、最終的なsqlステートメントは次のようになります。

    SELECT id,name,password,age,tel FROM user WHERE (age < ? OR age > ?)
    
3.1.5 無判定

まずは写真を見てみましょう。

ここに画像の説明を挿入します

  • 条件付きクエリを実行する場合、通常、ユーザーが選択してクエリするための条件が多数あります。
  • これらの条件を使用するかどうかはユーザーが選択できますが、たとえば、価格が 8,000 を超える携帯電話を調べたいとします。
  • 条件を入力する際、価格には幅がありますが、条件に応じて、最初の価格入力ボックスに 8000 と入力するだけで済みます。
  • バックグラウンドで価格の問い合わせを行う場合、通常は次のように尋ねられます。price>值1 and price <值2
  • フロントエンドは値2を入力しないので処理しないとprice>8000 and price < null問題が発生します
  • このとき、クエリ結果に問題が発生しますが、どうすれば解決できますか?

ここに画像の説明を挿入します

要件: データベース テーブルをクエリして、入力された年齢範囲に基づいて修飾されたレコードをクエリします。
ユーザーが値を入力するときに、
最初のボックスのみが入力されている場合は、その年齢よりも古いユーザーがクエリされることを意味します。2
番目のボックスのみの場合は、が入力されている場合は、この年齢より若いユーザーを照会することを示します。
両方のボックスに入力すると、2 つの範囲の間にある年齢のユーザーを照会することを意味します。

最初の質問について考えてみましょう。バックエンドがフロントエンドから 2 つのデータを受信したい場合、どのように受信すればよいでしょうか。

2 つの単純なデータ型を使用することも、モデル クラスを使用することもできますが、現在、User クラスには次のような age 属性が 1 つだけあります。

@Data
public class User {
    
    
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}

1 つのage属性を使用して、ページ上で 2 つの値を受け取るにはどうすればよいですか? 現時点では 2 つの解決策があります。

オプション 1: 属性の追加age2このアプローチは可能ですが、元のモデル クラスの属性コンテンツに影響します。

@Data
public class User {
    
    
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
    private Integer age2;
}

オプション 2: 新しいモデル クラスを作成し、クラスを継承させ、Userそれにage2属性を追加します。属性をUserQuery取得した後、同時に属性を追加します。Userage2

@Data
public class User {
    
    
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}

@Data
public class UserQuery extends User {
    
    
    private Integer age2;
}

環境の準備ができたら、次の要件を実装しましょう。

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        //模拟页面传递过来的查询数据
        UserQuery uq = new UserQuery();
        uq.setAge(10);
        uq.setAge2(30);
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        if(null != uq.getAge2()){
    
    
            lqw.lt(User::getAge, uq.getAge2());
        }
        if( null != uq.getAge()) {
    
    
            lqw.gt(User::getAge, uq.getAge());
        }
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
    }
}

上記の書き方で条件が空でないことの判定は完了しますが、問題は明らかで、条件が多い場合にはそれぞれの条件を判定する必要があり、コード量が比較的多くなります。私たちに提供された簡略化された方法MP:

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        //模拟页面传递过来的查询数据
        UserQuery uq = new UserQuery();
        uq.setAge(10);
        uq.setAge2(30);
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.lt(null!=uq.getAge2(),User::getAge, uq.getAge2());
        lqw.gt(null!=uq.getAge(),User::getAge, uq.getAge());
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
    }
}
  • lt()方法

ここに画像の説明を挿入します
conditionboolean型の場合、true返される場合は条件が追加され、返される場合はfalse条件は追加されません。

3.2 クエリプロジェクション

3.2.1 指定されたフィールドのクエリ

現在、データをクエリするときは何もせず、デフォルトではテーブル内のすべてのフィールドの内容をクエリします。クエリ プロジェクションと呼ばれるものは、すべてのフィールドをクエリするのではなく、指定されたコンテンツを持つデータのみをクエリします。

具体的にどうすれば達成できるのでしょうか?

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.select(User::getId,User::getName,User::getAge);
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
    }
}
  • select(...)このメソッドは、クエリのフィールド列を設定するために使用されます。複数のフィールドを設定できます。最後のsqlステートメントは次のとおりです。

    SELECT id,name,age FROM user
    
  • 使用しない場合はlambda、フィールドを手動で指定する必要があります

    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
          
          
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
          
          
            QueryWrapper<User> lqw = new QueryWrapper<User>();
            lqw.select("id","name","age","tel");
            List<User> userList = userDao.selectList(lqw);
            System.out.println(userList);
        }
    }
    
    • 最後のsqlステートメントは次のとおりです。SELECT id,name,age,tel FROM user
3.2.2 集計クエリ

要件: 集計関数のクエリ、count、max、min、avg、sum の使用を完了
count: レコードの総数
max: 最大値
min: 最小値
avg: 平均値
sum: 合計

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        QueryWrapper<User> lqw = new QueryWrapper<User>();
        //lqw.select("count(*) as count");
        //SELECT count(*) as count FROM user
        //lqw.select("max(age) as maxAge");
        //SELECT max(age) as maxAge FROM user
        //lqw.select("min(age) as minAge");
        //SELECT min(age) as minAge FROM user
        //lqw.select("sum(age) as sumAge");
        //SELECT sum(age) as sumAge FROM user
        lqw.select("avg(age) as avgAge");
        //SELECT avg(age) as avgAge FROM user
        List<Map<String, Object>> userList = userDao.selectMaps(lqw);
        System.out.println(userList);
    }
}

結果のカプセル化を容易にするために、後でこれらのデータを取得できるように、上記の集計関数に名前を付けました。

3.2.3 グループクエリ

要件: グループクエリ、完全なgroup byクエリの使用

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        QueryWrapper<User> lqw = new QueryWrapper<User>();
        lqw.select("count(*) as count,tel");
        lqw.groupBy("tel");
        List<Map<String, Object>> list = userDao.selectMaps(lqw);
        System.out.println(list);
    }
}
  • groupByがグループ化されている場合、最後のsqlステートメントは

    SELECT count(*) as count,tel FROM user GROUP BY tel
    

知らせ:

  • 集計およびグループ化クエリはlambda式を使用して完了できません
  • MPこれは正しいMyBatis機能拡張です。MP実装できない場合は、DAOインターフェイスに直接MyBatis実装できます。

3.3 クエリ条件

以前は と のみを使用していましたlt()gt()これら 2 つのメソッドに加えて、MP は条件に対応する多くのメソッドもカプセル化します。このセクションでは、MP が提供するクエリ条件メソッドを学習することに焦点を当てます。

MP多くのクエリ条件があります。

  • 範囲一致 (>、=、間)
  • ファジーマッチング(いいね)
  • 無効判定(無効)
  • 包含一致 (in)
  • グループ
  • 注文
  • ……
3.3.1 等価性クエリ

要件: ユーザー名とパスワードに基づいてユーザー情報をクエリする

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");
        User loginUser = userDao.selectOne(lqw);
        System.out.println(loginUser);
    }
}
  • eq(): と同等=、対応する SQL ステートメントは次のとおりです。

    SELECT id,name,password,age,tel FROM user WHERE (name = ? AND password = ?)
    
  • selectList: クエリ結果は複数または単一です

  • selectOne:クエリ結果は単一です

3.3.2 範囲クエリ

要件: 年齢に関する範囲クエリを実行し、lt()、le()、gt()、ge()、between() を使用して範囲クエリを実行します。

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.between(User::getAge, 10, 30);
        //SELECT id,name,password,age,tel FROM user WHERE (age BETWEEN ? AND ?)
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
    }
}
  • gt(): より大きい (>)
  • ge(): 以上 (>=)
  • lt(): より小さい (<)
  • lte(): 以下 (<=)
  • between():間? そして ?
3.3.3 ファジークエリ

要件:クエリテーブル内の name 属性値が で始まるJユーザー情報をクエリします。あいまいクエリには like を使用します。

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.likeLeft(User::getName, "J");
        //SELECT id,name,password,age,tel FROM user WHERE (name LIKE ?)
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
    }
}
  • like(): %J% など、前後にパーセント記号を追加します。
  • likeLeft(): %J のように、前にパーセント記号を追加します。
  • likeRight(): J% のように、その後にパーセント記号を追加します。
3.3.4 ソートクエリ

要件: すべてのデータをクエリし、ID で降順に並べ替えます。

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
    
    
        LambdaQueryWrapper<User> lwq = new LambdaQueryWrapper<>();
        /**
         * condition :条件,返回boolean,
         		当condition为true,进行排序,如果为false,则不排序
         * isAsc:是否为升序,true为升序,false为降序
         * columns:需要操作的列
         */
        lwq.orderBy(true,false, User::getId);

        userDao.selectList(lw
    }
}

上記の実装に加えて、図に示すように、呼び出すことができる他の並べ替えメソッドが多数あります。
ここに画像の説明を挿入します

  • orderBy選別
    • condition: 条件をtrue指定した場合は並べ替えが追加されます。falseそれ以外の場合は並べ替えは追加されません。
    • isAsc:昇順、true昇順、false降順のいずれか
    • columns: 並べ替えフィールド、複数存在する可能性があります
  • orderByAsc/Desc(単一column): 指定されたフィールドに従った昇順/降順
  • orderByAsc/Desc(複数column): 複数のフィールドに基づく昇順/降順
  • orderByAsc/Desc
    • condition: 条件、trueソートを追加する、falseソートを追加しない
    • 複数columns: 複数のフィールドで並べ替えます

上記で紹介したクエリ条件の構築方法以外にもさまざまな方法がありますので、isNull,isNotNull,in,notIn詳しくは公式ドキュメントの条件コンストラクタの使い方を参照してください。具体的なURLは
https://mp です。 baomidou.com/guide/wrapper.html#abstractwrapper

3.4 マッピングマッチングの互換性

以前は、テーブルからデータをクエリし、そのデータをモデル クラスにカプセル化することができました。このプロセス全体には、テーブルとモデル クラスが含まれます。
ここに画像の説明を挿入します

テーブルからデータを正常に取得してモデル オブジェクトにカプセル化できる理由は、テーブルのフィールド列名がモデル クラスの属性名と同じであるためです。

そこで次のような質問が起こります。

問題 1: テーブルのフィールドとエンコード属性の設計が同期していない

テーブルのカラム名とモデルクラスの属性名が一致しない場合、モデルオブジェクトにデータがカプセル化されないため、どちらかが修正する必要があります。どちらの側も変更できないという前提ですか?

@TableFieldMPは、モデル クラスの属性名とテーブルの列名の間のマッピング関係を実現するために使用できるアノテーションを提供します。
ここに画像の説明を挿入します

問題 2: データベース内の未定義のプロパティがエンコーディングに追加される

データベース テーブルに存在しない追加フィールドがモデル クラスにある場合、生成されたsqlステートメントはselectデータベースに存在しないフィールドをクエリし、プログラムの実行時にエラーが報告されます。 :

「フィールドリスト」の不明な列「追加フィールド名」

特定のソリューションでは引き続き注釈が使用されます。これには、「フィールドがデータベース テーブルに存在するかどうかを設定する」という@TableField属性があります。「はい」に設定されている場合、フィールドは存在しません。ステートメント クエリを生成するとき、フィールドは再度クエリされません。 。existfalsesql
ここに画像の説明を挿入します

質問 3: デフォルトのクエリを使用すると、より多くのフィールド表示権限が解放されます

テーブル内のすべての列のデータをクエリすると、一部の機密データがクエリされ、フロントエンドに返される可能性があります。このとき、デフォルトでクエリを実行しないフィールドを制限する必要があります。解決策は、フィールドの値をデフォルトでクエリする必要があるかどうかを設定する annotation という属性を追加することです。 true (デフォルト値) はフィールドがデフォルトでクエリされることを意味し、 false はフィールドがデフォルトでクエリされないことを意味し@TableFieldますselect
ここに画像の説明を挿入します

知識ポイント 1: @TableField
名前 @TableField
タイプ プロパティの注釈
位置 モデルクラス属性定義の上
効果 現在の属性に対応するデータベース テーブル内のフィールド関係を設​​定します。
関連するプロパティ value (デフォルト): データベース テーブルのフィールド名を設定します。exist:
データベース テーブルのフィールドに属性が存在するかどうかを設定します。デフォルトは true です。この属性は value と組み合わせることはできません。選択: 属性が
クエリに参加するかどうかを設定します。属性が select() マッピングで構成されていません。
問題 4: テーブル名とコーディング開発設計が同期していない

主な問題は、テーブルの名前がモデル クラスの名前と一致しないため、クエリが失敗することです。このとき、通常、次のエラー メッセージが報告されます。

テーブル「databaseName.tableNaem」が存在しませんこれは、データベース内にテーブルが存在しないことを意味します。

ここに画像の説明を挿入します

解決策は、テーブルとモデル クラス間の対応関係を設定するために提供MPされている別のアノテーションを使用することです。@TableName

ここに画像の説明を挿入します

知識ポイント 2: @TableName
名前 @テーブル名
タイプ クラスのアノテーション
位置 モデルクラス定義の上
効果 データベーステーブルのリレーションシップに対応するように現在のクラスを設定します。
関連するプロパティ 値 (デフォルト): データベーステーブル名を設定します。
コードデモ

次に、ケースを使用して、学んだばかりの知識を実証します。

ステップ 1: データベース テーブル ユーザーを tbl_user に変更します。

MPデフォルトではモデルクラス名の最初の文字がテーブル名と同じ小文字であるため、直接クエリではエラーが報告されます。
ここに画像の説明を挿入します

ステップ 2: @TableName アノテーションをモデル クラスに追加する
@Data
@TableName("tbl_user")
public class User {
    
    
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}
ステップ 3: フィールドのパスワードを pwd に変更します。

MPデフォルトでは、モデル クラスの属性名がテーブルの列名として使用されるため、直接クエリではエラーが報告されます。

ここに画像の説明を挿入します

ステップ 4: @TableField マッピング関係を使用する
@Data
@TableName("tbl_user")
public class User {
    
    
    private Long id;
    private String name;
    @TableField(value="pwd")
    private String password;
    private Integer age;
    private String tel;
}
ステップ 5: データベーステーブルに存在しないフィールドを追加する
@Data
@TableName("tbl_user")
public class User {
    
    
    private Long id;
    private String name;
    @TableField(value="pwd")
    private String password;
    private Integer age;
    private String tel;
    private Integer online;
}

直接クエリを実行するとエラーが報告されます。その理由は、MPデフォルトでは、モデル クラスのすべての属性に対応するデータベース テーブルの列がクエリされるが、onlineこれらの列は存在しないためです。

ここに画像の説明を挿入します

ステップ 6: @TableField を使用してフィールドを除外する
@Data
@TableName("tbl_user")
public class User {
    
    
    private Long id;
    private String name;
    @TableField(value="pwd")
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}
ステップ 7: クエリ時に pwd を非表示にする
@Data
@TableName("tbl_user")
public class User {
    
    
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}

4 DMLプログラミング制御

クエリ関連の操作はすでに紹介しましたが、残りの 3 つの追加、削除、変更の内容について説明する必要があります。一つずつ説明していきますが、まずはinsert()内の内容を追記していきます。

4.1 ID生成ポリシー制御

以前に新しいものを追加したときに問題が残されました。つまり、新しい追加が成功した後、主キー ID は非常に長い内容の文字列になります。さらに必要なのは、データベース テーブルのフィールドに従って自動インクリメントすることです。この問題を解決するには、まずID選択方法を分析しましょう。

  • テーブルごとに異なる ID 生成戦略が適用されます
    • ログ: 自動インクリメント (1、2、3、4、…)
    • 注文書: 特別ルール (FQ23948AK3843)
    • テイクアウト注文:関連エリアの日付とその他の情報(10 04 20200314 34 91)
    • 関係表:idは省略可能
    • ……

企業ごとに異なる ID 生成方法を使用する必要があるため、MP ではどのような主キー生成戦略が提供されており、どのように選択すればよいでしょうか?

ここでは、MP のアノテーションを使用する必要があります。@TableId

知識ポイント 1: @TableId
名前 @テーブルID
タイプ プロパティの注釈
位置 主キーを表すために使用されるモデル クラスの属性定義の上
効果 現在のクラスの主キー属性の生成戦略を設定します。
関連するプロパティ value (デフォルト): データベーステーブルの主キー名を設定します。
type: 主キー属性の生成戦略を設定します。値は IdType の列挙値に基づきます。
4.1.1 環境構築

条件付きクエリを構築する前に、まず環境を準備しましょう

  • SpringBootプロジェクトを作成する

  • pom.xml対応する依存関係を追加します

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.5.0</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.itheima</groupId>
        <artifactId>mybatisplus_03_dml</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
    
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.4.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.16</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    
  • 書き込みUserDaoインターフェース

    @Mapper
    public interface UserDao extends BaseMapper<User> {
          
          
    }
    
  • モデルクラスを書く

    @Data
    @TableName("tbl_user")
    public class User {
          
          
        private Long id;
        private String name;
        @TableField(value="pwd",select=false)
        private String password;
        private Integer age;
        private String tel;
        @TableField(exist=false)
        private Integer online;
    }
    
  • ブートクラスの書き込み

    @SpringBootApplication
    public class Mybatisplus03DqlApplication {
          
          
    
        public static void main(String[] args) {
          
          
            SpringApplication.run(Mybatisplus03DqlApplication.class, args);
        }
    
    }
    
  • 設定ファイルの書き込み

    # dataSource
    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
        username: root
        password: root
    # mp日志
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
  • テストクラスを書く

    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
          
          
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
          
          
            List<User> userList = userDao.selectList(null);
            System.out.println(userList);
        }
    }
    
  • テスト

    @SpringBootTest
    class Mybatisplus03DqlApplicationTests {
          
          
    
        @Autowired
        private UserDao userDao;
    	
        @Test
        void testSave(){
          
          
            User user = new User();
            user.setName("黑马程序员");
            user.setPassword("itheima");
            user.setAge(12);
            user.setTel("4006184000");
            userDao.insert(user);
        }
        @Test
        void testDelete(){
          
          
            userDao.deleteById(1401856123925713409L)
        }
        @Test
        void testUpdate(){
          
          
            User user = new User();
            user.setId(3L);
            user.setName("Jock666");
            user.setVersion(1);
            userDao.updateById(user);
        }
    }
    
  • 作成される最終的なプロジェクト構造は次のとおりです。
    ここに画像の説明を挿入します

4.1.2 コードのデモ
オート戦略
ステップ 1: ビルド戦略を AUTO に設定する
@Data
@TableName("tbl_user")
public class User {
    
    
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}
ステップ 2: テスト データを削除し、自動インクリメント値を変更する
  • テストデータの削除
    ここに画像の説明を挿入します

  • 以前に生成された主キーの値がID比較的長いため、MySQL自動的に増加する値が非常に大きくなるため、最新のid値に調整する必要があります。
    ここに画像の説明を挿入します

ステップ 3: 新しいメソッドを実行する

新しい追加が成功し、主キーも最初idからであることがわかります。これらの 3 つの手順のデモンストレーションの後、効果が次のようになっていることがわかります。5
ここに画像の説明を挿入します
AUTOデータベース ID を使用して自動的に増加しますこの戦略を使用する場合は、対応するデータベース テーブルに主キーの自動インクリメント セットがあることを必ず確認してくださいID。そうでない場合は無効になります。

次に、ソース コードを入力して確認できるID生成戦略は何でしょうか?

ソース コードを開くと、中国語のコメントが表示されないことがわかります。これには、右上隅をクリックする必要があり、Download Sourcesこのjavaクラスのファイルが自動的にダウンロードされ、特定のコメントの内容が表示されます。このテクノロジーは中国人によって作成されたため、コード内のコメントは比較的理解しやすいです。
ここに画像の説明を挿入します
ソース コードをダウンロードすると、次の内容が表示されます。
ここに画像の説明を挿入します

ソース コードからわかるように、AUTOこの戦略に加えて、次の生成戦略もあります。

  • NONE:idビルド戦略を設定しません
  • INPUT:ユーザーマニュアル入力id
  • ASSIGN_ID:スノーフレークアルゴリズム生成id(数値型および文字列型と互換性あり)
  • ASSIGN_UUID: UUIDID 生成戦略として生成アルゴリズムを使用します。
  • 他のいくつかの戦略は廃止され、ASSIGN_IDとにASSIGN_UUID置き換えられます。

拡大する:

何が配布されているのですIDか?

  • データの量が大きい場合、1 台のデータベース サーバーではデータを保存できず、複数のデータベース サーバーが必要になります。
  • たとえば、注文テーブルが異なるサーバーに保存される場合があります。
  • データベース テーブルの自動インクリメント主キーを使用すると、2 つのサーバー上にあるため競合が発生します。
  • このとき、分散されるグローバルな一意性が必要とIDなりIDますID
インプット戦略
ステップ 1: ビルド戦略を INPUT に設定する
@Data
@TableName("tbl_user")
public class User {
    
    
    @TableId(type = IdType.INPUT)
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}

: このID生成戦略では、テーブルの自動インクリメント戦略を削除する必要があります。
ここに画像の説明を挿入します

ステップ 2: データを追加して ID を手動で設定する
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
	
    @Test
    void testSave(){
    
    
        User user = new User();
        //设置主键ID的值
        user.setId(666L);
        user.setName("黑马程序员");
        user.setPassword("itheima");
        user.setAge(12);
        user.setTel("4006184000");
        userDao.insert(user);
    }
}
ステップ 3: 新しいメソッドを実行する

主キーの値が設定されていない場合ID、エラー メッセージが報告され、主キーにID値がないことが示されます。
ここに画像の説明を挿入します

主キーが設定されている場合ID、次のようにデータは正常に追加されます。
ここに画像の説明を挿入します

ASSIGN_ID 戦略
ステップ 1: 生成戦略を ASSIGN_ID に設定する
@Data
@TableName("tbl_user")
public class User {
    
    
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}
ステップ2:IDを設定せずにデータを追加する
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
	
    @Test
    void testSave(){
    
    
        User user = new User();
        user.setName("黑马程序员");
        user.setPassword("itheima");
        user.setAge(12);
        user.setTel("4006184000");
        userDao.insert(user);
    }
}

: この生成戦略は手動で設定する必要はありませんID。手動で設定した場合はID、ユーザーが設定した値が使用されます。

ステップ 3: 新しいメソッドを実行する

ここに画像の説明を挿入します

生成されるのは一種のデータIDですLong

ASSIGN_UUID 戦略
ステップ 1: 生成戦略を ASSIGN_UUID に設定する

使用する際の注意点uuidは、主キーの型を にすることはできず、Longに変更する必要があることです。String

@Data
@TableName("tbl_user")
public class User {
    
    
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}
ステップ 2: テーブルの主キーのタイプを変更する

ここに画像の説明を挿入します

生成される主キーは 32 ビットであるため、主キーの種類はvarchar32 より大きい長さに設定されてUUIDいます。長さが小さい場合、挿入は失敗します。

ステップ3:IDを設定せずにデータを追加する
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
	
    @Test
    void testSave(){
    
    
        User user = new User();
        user.setName("黑马程序员");
        user.setPassword("itheima");
        user.setAge(12);
        user.setTel("4006184000");
        userDao.insert(user);
    }
}
ステップ 4: 新しいメソッドを実行する

ここに画像の説明を挿入します

次に、スノーフレーク アルゴリズムについて説明します。

Snowflake アルゴリズム ( SnowFlake) は、でTwitter書かれた公式のアルゴリズム実装ですScala生成された結果は 64 ビット整数で、その構造は次のとおりです。
ここに画像の説明を挿入します

  1. 2 進数の最上位ビットは符号ビットであり、1 は負の数を表し、0 は正の数を表すため、1 ビットは使用されません。生成される ID は通常整数であるため、最上位ビットは 0 に固定されます。
  2. 41ビットタイムスタンプ、ミリ秒レベルのタイムスタンプを記録するために使用されます
  3. 10 ビット - 稼働マシン ID。稼働マシン ID を記録するために使用されます。上位 5 ビットはデータ センター ID で、値の範囲は 0 ~ 31 です。下位 5 ビットは稼働ノード ID で、その値の範囲は次のとおりです。 0 ~ 31。この 2 つの組み合わせにより、最大 1024 ノードに対応できます。
  4. シリアル番号は 12 ビットを占め、各ノードは 0 から最大 4095 までミリ秒ごとに蓄積を開始し、合計 4096 個の ID を生成できます。
4.1.3 ID 生成戦略の比較

これらの主キー ID 生成戦略が導入されましたが、今後はどれを使用する必要がありますか?

  • NONE: ID 生成戦略が設定されておらず、INPUT とほぼ等しい MP が自動生成されないため、どちらの方法でもユーザーが手動で設定する必要がありますが、手動設定の最初の問題は、同じ ID が発生しやすいことです。主キーが競合しないようにするには、多くの判断が必要となり、実装がより複雑になります。
  • AUTO: データベース ID は自動的に増加します。この戦略は、データベース サーバーが 1 台しかない場合の使用に適しています。分散 ID としては使用できません。
  • ASSIGN_UUID: 分散状況で使用でき、一意性を保証できますが、生成される主キーは 32 ビット文字列であり、長すぎて領域を占有し、並べ替えることができません。また、クエリのパフォーマンスも遅くなります。
  • ASSIGN_ID: 分散状況で使用できます。Long 型の数値を生成します。これはソート可能で、パフォーマンスが高いです。ただし、生成された戦略はサーバー時間に関連します。システム時間が変更されると、プライマリが重複する可能性があります。キー。
  • まとめると、各主キー戦略にはそれぞれ長所と短所があり、プロジェクトのビジネスの実情に応じて選択して使用するのが賢明です。
4.1.4 簡素化された構成

これで、テーブル リレーションシップ マッピングとデータベースの主キー戦略の設定が完了しました。次に、これら 2 つのコンテンツを使用するための、簡略化された構成について説明します。

モデルクラスの主キーポリシー設定

主キー ID の戦略が導入されましたが、次のようなプロジェクト内のすべてのモデル クラスに同じ生成戦略を使用したい場合は、[外部リンク画像の転送に失敗しました。元のサイトにはリーチ防止メカニズムがある可能性があります。画像を転送して直接保存してアップロードすることをお勧めします
ここに画像の説明を挿入します

確かに少し面倒ですが、すべてのモデル クラスがこの主キー ID 戦略を使用できるように、どこかで設定できないでしょうか?

答えは「はい」です。次の内容を構成ファイルに追加するだけです。

mybatis-plus:
  global-config:
    db-config:
    	id-type: assign_id

構成が完了すると、各モデル クラスの主キー ID 戦略は assign_id になります。

データベーステーブルとモデルクラス間のマッピング関係

MP は、デフォルトでモデル クラス名の最初の文字をテーブル名として使用します。データベース テーブルの名前がす​​べて で始まる場合は、次のようにそれtbl_をすべてのモデル クラスに追加する必要があります。@TableName
ここに画像の説明を挿入します

設定はまだ比較的面倒ですが、設定ファイルに次の内容を設定するのが簡単な方法です。

mybatis-plus:
  global-config:
    db-config:
    	table-prefix: tbl_

tbl_MPがモデル クラスの最初の文字を小文字で追加してデータベースのテーブル名に組み立てられるように、テーブルのプレフィックスの内容を設定します。

4.2 複数のレコード操作

まず質問を見てみましょう。
ここに画像の説明を挿入します

以前にショッピング カートにたくさんの商品を追加しましたが、数日後に不要になったことがわかりました。どうすればよいですか?

削除は非常に簡単ですが、1 つずつ削除するのはまだ比較的遅くて面倒なので、通常はユーザーにバッチ操作が与えられます。つまり、先頭にチェック ボックスがあり、ユーザーは一度に複数またはすべてをチェックできます。を選択して削除する ショッピング カートを一度に空にすることができます批量删除

具体的に複数の削除を実装する方法については、対応する API メソッドを見つけてみましょう。

int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

変換メソッドの文字通りの意味は次のとおりです: delete (ID に基づいて一括削除) パラメータは複数の ID 値を格納できるコレクションです。

要件: 渡された ID セットに従ってデータベース テーブル内のデータを削除します。

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
	
    @Test
    void testDelete(){
    
    
        //删除指定多条数据
        List<Long> list = new ArrayList<>();
        list.add(1402551342481838081L);
        list.add(1402553134049501186L);
        list.add(1402553619611430913L);
        userDao.deleteBatchIds(list);
    }
}

実行が成功すると、データベース テーブル内のデータが指定された ID に従って削除されます。

ID セットに基づいて一括削除するだけでなく、ID セットに基づいてバッチ クエリを実行することもできます。まずは API を見てみましょう。

List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

メソッド名は次のように変換されます: query (ID に基づくバッチ クエリ) パラメーターは、複数の ID 値を格納できるコレクションです。

要件: 受信した ID セットに基づいてユーザー情報をクエリする

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
	
    @Test
    void testGetByIds(){
    
    
        //查询指定多条数据
        List<Long> list = new ArrayList<>();
        list.add(1L);
        list.add(3L);
        list.add(4L);
        userDao.selectBatchIds(list);
    }
}

クエリ結果は、渡された指定された ID 値に従ってクエリされます。

ここに画像の説明を挿入します

4.3 論理的な削除

次に、削除において最も重要な操作の 1 つである論理削除について説明します。まず問題を分析しましょう。
ここに画像の説明を挿入します

  • これは従業員と彼らが署名した契約テーブルです。従業員は複数の契約に署名できるという関係です。これは 1 (従業員) 対多 (契約) テーブルです。

  • ID 1 の従業員は合計 3 つの契約を結んでいますが、この時点で退職した場合は、従業員テーブルのデータを削除して削除操作を実行する必要があります。

  • 設計時にテーブルに主キーと外部キーの関係がある場合は、コントラクト テーブルの最初の 3 つのデータも削除する必要があります。
    ここに画像の説明を挿入します

  • 後で、署名された契約の合計金額を計算しようとすると、従業員 1 が署名した契約情報が削除されているため、一致しないことがわかります。

  • 従業員だけを削除して契約表データを削除しないと、契約の従業員番号に対応する従業員情報が存在せず、ジャンクデータが表示され、オーナーレス契約となり、存在が分からなくなります。張儀の。

  • したがって、分析後はテーブル内のデータを削除せずに保持する必要がありますが、退職者と在職者を区別する必要があるため、上記のような問題が解決されます。
    ここに画像の説明を挿入します

  • 見分ける方法は、社員テーブルにデータ列を追加することですdeleted。0なら現社員、退職なら1に変更します。(0と1の意味はカスタマイズ可能)

したがって、削除操作には次のようなビジネス上の問題があります。

  • 物理的な削除: ビジネス データがデータベースから破棄され、削除操作が実行されます。
  • 論理削除: データの使用可能ステータス・フィールドを設定します。削除する場合、ステータス・フィールドを使用不可ステータスに設定します。データはデータベースに保持され、更新操作が実行されます。

MP で論理削除を実装するにはどうすればよいですか?

ステップ 1: データベーステーブルを変更してdeleted列を追加する

フィールド名は任意で、0通常や1削除などの内容もカスタマイズ可能で、列を追加し、0同時にデフォルト値を通常に設定することもできます。
ここに画像の説明を挿入します

ステップ 2: エンティティ クラスに属性を追加する

(1) データベーステーブルの列に対応する属性名を追加します。名前は任意で構いません。データテーブルの列名と一致しない場合は、リレーションシップマッピングに @TableField を使用できます。一貫性がある場合は、自動的に対応させていただきます。

(2) 新しく追加されたフィールドを廃棄フィールドとしてマークし、次を使用します。@TableLogic

@Data
//@TableName("tbl_user") 可以不写是因为配置了全局配置
public class User {
    
    
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
    @TableLogic(value="0",delval="1")
    //value为正常数据的值,delval为删除数据的值
    private Integer deleted;
}
ステップ 3: 削除メソッドを実行する
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
	
    @Test
    void testDelete(){
    
    
       userDao.deleteById(1L);
    }
}

ここに画像の説明を挿入します

テスト結果から判断すると、論理削除の最後のステップは更新操作であり、指定されたフィールドを削除された状態に対応する値に変更します。

考える

論理的な削除はクエリに影響を与えますか?

  • クエリ操作の実行

    @SpringBootTest
    class Mybatisplus03DqlApplicationTests {
          
          
    
        @Autowired
        private UserDao userDao;
    	
        @Test
        void testFind(){
          
          
           System.out.println(userDao.selectList(null));
        }
    }
    

    テストを実行すると、出力される SQL ステートメントに次のような追加のクエリ条件があることがわかります。
    ここに画像の説明を挿入します

    MP の論理削除により、すべてのクエリに非削除条件が追加されることが考えられます。つまり、削除されたデータはクエリされるべきではありません。

  • 削除されたすべてのデータを引き続きクエリしたい場合はどうすればよいでしょうか?

    @Mapper
    public interface UserDao extends BaseMapper<User> {
          
          
        //查询所有数据包含已经被删除的数据
        @Select("select * from tbl_user")
        public List<User> selectAll();
    }
    
  • 各テーブルの論理的な削除が必要な場合は、各モデル クラスの属性に@TableLogicアノテーションを追加する必要があります。

    次のように構成ファイルにグローバル構成を追加します。

    mybatis-plus:
      global-config:
        db-config:
          # 逻辑删除字段名
          logic-delete-field: deleted
          # 逻辑删除字面值:未删除为0
          logic-not-delete-value: 0
          # 逻辑删除字面值:删除为1
          logic-delete-value: 1
    

論理削除の導入後、論理削除の本質は次のとおりです。

論理削除の本質は、実際には変更操作です。トゥームストーン フィールドが追加されると、データのクエリ時にトゥームストーン フィールドも自動的に含まれます。

実行される SQL ステートメントは次のとおりです。

tbl_user セットを更新します削除されました=1 ここで、id = ? そして削除されました=0

実行データの結果は次のとおりです。
ここに画像の説明を挿入します

知識ポイント 1: @TableLogic
名前 @TableLogic
タイプ プロパティの注釈
位置 削除されたフィールドを表すために使用されるモデル クラスの属性定義の上
効果 フィールドをトゥームストーン削除のフィールドとして識別します
関連するプロパティ value: 論理的に削除された値
delval: 論理的に削除された値

4.4 楽観的ロック

4.4.1 コンセプト

楽観的ロックについて説明する前に、まず問題を分析しましょう。

ビジネスの同時実行によって引き起こされる問題:フラッシュセール

  • 100 個の商品やチケットが販売されている場合、それぞれの商品やチケットを 1 人しか購入できないようにするには、買いすぎや重複販売が発生しないようにするにはどうすればよいでしょうか?
  • この種の問題に対しては、実際には多くの解決策が利用可能です
  • 最初に思い浮かぶのはロックです。1 つのサーバーでロックされている場合は解決できますが、複数のサーバーでロックされている場合は制御する方法がありません。たとえば、12306 にはチケットを販売するサーバーが 2 つあります。ロックを追加すると、両方に接続すると、2 つのスレッドが同時にチケットを販売する可能性があり、同時実行の問題が引き続き発生します。
  • 次に紹介する方法はデータベース自体の性能がボトルネックとなるため、小規模企業向けの解決策となりますが、同時実行数が2,000を超える場合は他の解決策を検討する必要があります。

簡単に言えば、楽観的ロックによって解決される主な問題は、レコードが更新されるときに、そのレコードが他のユーザーによって更新されないことが望ましいということです。

4.4.2 実装のアイデア

楽観的ロックを実装する方法:

  • バージョン列をデータベーステーブルに追加します。たとえば、デフォルト値は 1 です。
  • 最初のスレッドがデータを変更する前に、レコードをフェッチするときに、現在のデータベースで version=1 を取得します。
  • 2 番目のスレッドがデータを変更する前に、レコードをフェッチするときに、現在のデータベースで version=1 を取得します。
  • 最初のスレッドが更新を実行するとき、バージョン = newVersion (バージョン = oldVersion) に設定します。
    • newVersion = バージョン+1 [2]
    • oldVersion = バージョン [1]
  • 2 番目のスレッドが更新を実行するときは、バージョン = newVersion (バージョン = oldVersion) に設定します。
    • newVersion = バージョン+1 [2]
    • oldVersion = バージョン [1]
  • 両方のスレッドがデータを更新する場合、最初と 2 番目のスレッドの両方が最初に実行される可能性があります。
    • 最初のスレッドが最初に更新を実行すると、バージョンは 2 に変更されます。
    • 2 番目のスレッドが再び更新するときは、バージョン = 1 であるバージョン = 2 を設定します。この時点では、データベース テーブルのデータ バージョンはすでに 2 であるため、2 番目のスレッドはそれを変更できません。
    • 2 番目のスレッドが最初に更新を実行すると、バージョンは 2 に変更されます。
    • 最初のスレッドが再度更新するときは、バージョン = 1 であるバージョン = 2 を設定します。この時点では、データベース テーブルのデータ バージョンはすでに 2 であるため、最初のスレッドはそれを変更できません。
    • 誰が最初に実行しても、データを更新できるスレッドは 1 つだけであることが保証されます。これは、MP が提供する楽観的ロックの実装原理の分析です。

上記の手順をどのように実装すればよいでしょうか?

4.4.3 実装手順

手順を分析した後の具体的な実装手順は次のとおりです。

ステップ 1: データベーステーブルに列を追加する

列名は任意に指定できます。たとえば、 を使用してversion、列のデフォルト値を次のように設定します。1
ここに画像の説明を挿入します

ステップ 2: 対応する属性をモデル クラスに追加する

追加されたフィールド列名に従って、モデルクラスに対応する属性値を追加します

@Data
//@TableName("tbl_user") 可以不写是因为配置了全局配置
public class User {
    
    
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
    private Integer deleted;
    @Version
    private Integer version;
}
ステップ 3: 楽観的ロック用のインターセプターを追加する
@Configuration
public class MpConfig {
    
    
    @Bean
    public MybatisPlusInterceptor mpInterceptor() {
    
    
        //1.定义Mp拦截器
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
        //2.添加乐观锁拦截器
        mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mpInterceptor;
    }
}
ステップ 4: 更新操作を実行する
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
	
    @Test
    void testUpdate(){
    
    
       User user = new User();
        user.setId(3L);
        user.setName("Jock666");
        userDao.updateById(user);
    }
}

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムが備わっている可能性があります。画像を保存して直接アップロードすることをお勧めします (img-tfkk0Kak-1689850574034)(assets/1631252305080.png)]

この変更ではバージョン データが含まれていないため、バージョン フィールドは更新されないことがわかります。

バージョンデータの追加

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
	
    @Test
    void testUpdate(){
    
    
        User user = new User();
        user.setId(3L);
        user.setName("Jock666");
        user.setVersion(1);
        userDao.updateById(user);
    }
}

ここに画像の説明を挿入します
渡した値は 1 であることがわかります。MP は 1 ずつ増分し、それをデータベース テーブルに更新します。

したがって、オプティミスティック ロックを実装したい場合は、最初のステップとしてテーブル内のバージョンを取得し、次にそのバージョンを条件として使用してバージョンに 1 を追加し、それをデータベース テーブルに更新して戻す必要があります。そのため、クエリを実行すると、次のようになります。それを問い合わせる必要があります。

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
	
    @Test
    void testUpdate(){
    
    
        //1.先通过要修改的数据id将当前数据查询出来
        User user = userDao.selectById(3L);
        //2.将要修改的属性逐一设置进去
        user.setName("Jock888");
        userDao.updateById(user);
    }
}

ここに画像の説明を挿入します

楽観的ロックの実装手順を大まかに分析した後、ロックの状況をシミュレーションして、複数の人が同じデータを変更するときに 1 人だけが正常に変更できるかどうかを確認してみましょう。

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
    
    

    @Autowired
    private UserDao userDao;
	
    @Test
    void testUpdate(){
    
    
       //1.先通过要修改的数据id将当前数据查询出来
        User user = userDao.selectById(3L);     //version=3
        User user2 = userDao.selectById(3L);    //version=3
        user2.setName("Jock aaa");
        userDao.updateById(user2);              //version=>4
        user.setName("Jock bbb");
        userDao.updateById(user);               //verion=3?条件还成立吗?
    }
}

プログラムを実行して結果を分析します。
ここに画像の説明を挿入します

楽観的ロックが実装されましたが、上記の手順を思い出せない場合はどうすればよいですか?

実装するには公式ドキュメントを参照してください。

https://mp.baomidou.com/guide/interceptor-optimistic-locker.html#optimisticlockerinnerinterceptor
ここに画像の説明を挿入します

5 急速な発展

5.1 コード生成原理の分析

文章作成:ここに画像の説明を挿入します
空白のコンテンツを単語や文章で埋めることができます。例: [外部リンクの画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします。ここに画像の説明を挿入します

例: [外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムが組み込まれている可能性があります。画像を保存して直接アップロードすることをお勧めします。ここに画像の説明を挿入します

以前に書いたコードを観察すると、次のような繰り返しの内容がたくさんあることがわかります。

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムが備わっている可能性があります。画像を保存して直接アップロードすることをお勧めします。ここに画像の説明を挿入します

Bookそこで、Book モジュールを開発したい場合は、次のような赤い部分の内容をすべて置き換えるだけでよいのではないかと考えました。

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムが備わっている可能性があります。画像を保存して直接アップロードすることをお勧めします。ここに画像の説明を挿入します

したがって、モジュールを開発するとき、このコードは基本的に赤い部分を調整するものであることがわかります。そのため、赤い内容を削除するものを呼び出します。テンプレート、赤い部分はと呼ばれますパラメータ、将来的には、さまざまなパラメーターを渡すだけで済み、テンプレートに基づいてさまざまなモジュールの dao コードを作成できるようになります。

Dao のモジュール抽出機能に加えて、実際には、公開部分がある限り、すべての共通クラスを抽出できます。モデルクラスのテンプレートを見てみましょう。
ここに画像の説明を挿入します

  • ① データベーステーブルのテーブル名に従って入力可能
  • ② ユーザー設定に応じてID生成戦略を生成可能
  • ③~⑨はデータベーステーブルのフィールド名に基づいて入力できます。

したがって、コードを生成しているテーブルがわかっていれば、これらの内容を入力できます。

分析の結果、自動コード生成を完了するには次のものが必要であることがわかります。

  • テンプレート:MyBatisPlus が提供します。自分で用意することもできますが、面倒なのでお勧めしません。
  • データベース関連の設定: データベースを読み取り、テーブルとフィールドの情報を取得します。
  • 開発者定義の構成: ID 生成戦略などの手動構成

5.2 コードジェネレータの実装

ステップ 1: Maven プロジェクトを作成する
コード 2: 対応する jar パッケージをインポートする
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.1</version>
    </parent>
    <groupId>com.itheima</groupId>
    <artifactId>mybatisplus_04_generator</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--spring webmvc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--mybatisplus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>

        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

        <!--代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>

        <!--velocity模板引擎-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.3</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
ステップ 3: ブートストラップ クラスを作成する
@SpringBootApplication
public class Mybatisplus04GeneratorApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(Mybatisplus04GeneratorApplication.class, args);
    }
}
ステップ 4: コード生成クラスを作成する
public class CodeGenerator {
    
    
    public static void main(String[] args) {
    
    
        //1.获取代码生成器的对象
        AutoGenerator autoGenerator = new AutoGenerator();

        //设置数据库相关配置
        DataSourceConfig dataSource = new DataSourceConfig();
        dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        autoGenerator.setDataSource(dataSource);

        //设置全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatisplus_04_generator/src/main/java");    //设置代码生成位置
        globalConfig.setOpen(false);    //设置生成完毕后是否打开生成代码所在的目录
        globalConfig.setAuthor("黑马程序员");    //设置作者
        globalConfig.setFileOverride(true);     //设置是否覆盖原始生成的文件
        globalConfig.setMapperName("%sDao");    //设置数据层接口名,%s为占位符,指代模块名称
        globalConfig.setIdType(IdType.ASSIGN_ID);   //设置Id生成策略
        autoGenerator.setGlobalConfig(globalConfig);

        //设置包名相关配置
        PackageConfig packageInfo = new PackageConfig();
        packageInfo.setParent("com.aaa");   //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
        packageInfo.setEntity("domain");    //设置实体类包名
        packageInfo.setMapper("dao");   //设置数据层包名
        autoGenerator.setPackageInfo(packageInfo);

        //策略设置
        StrategyConfig strategyConfig = new StrategyConfig();
        strategyConfig.setInclude("tbl_user");  //设置当前参与生成的表名,参数为可变参数
        strategyConfig.setTablePrefix("tbl_");  //设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名  例如: User = tbl_user - tbl_
        strategyConfig.setRestControllerStyle(true);    //设置是否启用Rest风格
        strategyConfig.setVersionFieldName("version");  //设置乐观锁字段名
        strategyConfig.setLogicDeleteFieldName("deleted");  //设置逻辑删除字段名
        strategyConfig.setEntityLombokModel(true);  //设置是否启用lombok
        autoGenerator.setStrategy(strategyConfig);
        //2.执行生成操作
        autoGenerator.execute();
    }
}

コードジェネレーターのコード内容については、公式ドキュメントから直接コードを取得して修正することができます。

https://mp.baomidou.com/guide/generator.html

ステップ 5: プログラムを実行する

正常に実行されると、現在のプロジェクトに大量のコードが生成されます。コードにはcontrollerservicemapperおよび が含まれます。entity

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムが備わっている可能性があります。画像を保存して直接アップロードすることをお勧めします。ここに画像の説明を挿入します

この時点で、コード ジェネレーターの作業が完了したので、データベース テーブルに基づいて対応するクラスをすばやく作成できるため、コード開発が簡素化されます。

5.3 MP のサービスの CRUD

ビジネス層コードの以前の記述を確認し、インターフェイスと対応する実装クラスを記述します。

public interface UserService{
    
    
	
}

@Service
public class UserServiceImpl implements UserService{
    
    

}

インターフェースと実装クラスを確立したら、インターフェースと実装クラスでメソッドを宣言する必要があります。

public interface UserService{
    
    
	public List<User> findAll();
}

@Service
public class UserServiceImpl implements UserService{
    
    
    @Autowired
    private UserDao userDao;
    
	public List<User> findAll(){
    
    
        return userDao.selectList(null);
    }
}

MP が上記のコードを見た後、これらのメソッドは比較的固定的で汎用的であるため、抽出させてください。したがって、MP は Service インターフェイスと実装クラスをそれぞれ提供します。そして、後者は前者の特定のバージョンですIServiceServiceImpl. 達成する。

将来的には、自分たちで作成したサービスを次のように変更できるようになります。

public interface UserService extends IService<User>{
    
    
	
}

@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService{
    
    

}

変更後の利点は、MP がビジネス層でのいくつかの基本的な追加、削除、変更、チェックの実装に役立ち、直接使用できることです。

テスト用のテスト クラスを作成します。

@SpringBootTest
class Mybatisplus04GeneratorApplicationTests {
    
    

    private IUserService userService;

    @Test
    void testFindAll() {
    
    
        List<User> list = userService.list();
        System.out.println(list);
    }

}

**注意:** MyBatis 環境は、mybatisplus_04_generator プロジェクトで構成されていません。これを実行する場合は、実行する前に構成ファイルの内容を抽出して完了する必要があります。

考察: MP によってカプセル化されたサービス層ではどのようなメソッドが使用できるでしょうか?

公式ドキュメントを確認してください: https://mp.baomidou.com/guide/crud-interface.html
これらの提供されたメソッドを学習して使用するには、公式ドキュメントを参照してください。メソッドの名前は変更される場合がありますが、パラメータと戻り値は異なります。各メソッドに対応する値は基本的に同じです。

おすすめ

転載: blog.csdn.net/CSDN_Admin0/article/details/131837610