Mybatis-Plus初心者入門、1記事で十分

目次

1. MyBatis-Plus の概要

1 はじめに

2. 特徴

3. サポートデータベース

4. フレーム構造

5.正式な住所

2. 導入事例

1. 開発環境

2. データベースとテーブルの構築

3. プロジェクトを作成する

4. エンコーディングの設定

1.ベースマッパー

5. テストクエリ

3、追加、削除、変更、確認

1.ベースマッパー

2. マッパー層を呼び出して CRUD を実装する

2.1 インサート

2.2 削除

2.3 変更点

2.4 クエリ

3. 一般的なサービス

4.サービス層を呼び出してデータを操作する

4、共通の注釈

1. @テーブル名

1.1 問題の原因

1.2 問題解決

2.@テーブルID

2.1 問題の原因

2.2 問題解決

2.3 @TableId の value 属性

2.4 @TableId の type 属性

3.@TbaleField

3.1 ケース 1

3.2 ケース 2

4.@テーブルロジック

4.1 墓石

4.2 論理削除の実現

5. 条件コンストラクター

1. ラッパーの紹介

2.クエリラッパー

3.UpdateWrapper

4.条件パラメータ

5.LambdaQueryWrapper

6.LambdaUpdateWrapper

よく使用される 6 つのプラグイン

1. ページネーションプラグイン

2. カスタムページネーション

3. 楽観的ロック

3.1 シナリオ

3.2 楽観的ロックと悲観的ロック

3.3 変更の競合をシミュレートする

3.4 楽観的ロックが問題を解決する

七、一般列挙

8. コードジェネレーター

1. 依存関係を導入する

2. 高速生成

9 つの複数のデータ ソース

1. データベースとテーブルを作成する

2. 新しいプロジェクトで依存関係が導入される

3. 設定ファイルを書き込む

4. エンティティクラスの作成

5. マッパーとサービスの作成

6. テストメソッドを書く

10.MyBatisXプラグイン

1.MyBatisXプラグインをインストールします

2. コードを迅速に生成する

3. CRUDを迅速に生成する



1. MyBatis-Plus の概要

1 はじめに

MyBatis-Plus (新しいウィンドウで開きます) (略して MP) は、MyBatis (新しいウィンドウで開きます)の拡張ツールであり、MyBatis をベースに、変更を加えずに拡張のみを行い、開発の簡素化と効率の向上を目的として生まれました。

私たちのビジョンは、魂斗羅の 1P と 2Pのように、友達とペアを組むと効率が 2 倍になる、MyBatis のベストパートナーになることです。

2. 特徴

  • 侵入なし: 機能強化のみで変更はありません。導入しても既存のプロジェクトには影響せず、シルクのようにスムーズです。

  • 低損失: 基本的な CURD は起動時に自動的に挿入され、パフォーマンスは基本的に損失がなく、オブジェクト指向の操作が直接実行されます。

  • 強力な CRUD 操作: 汎用マッパーと汎用サービスが組み込まれており、単一テーブルに対する CRUD 操作のほとんどがわずかな設定で実現でき、さまざまな使用ニーズを満たす強力な条件コンストラクターがあります。

  • Lambdaフォーム呼び出しのサポート:Lambda式を介して、さまざまなクエリ条件を記述するのが便利で、フィールドのタイプミスを心配する必要はありません

  • 自動主キー生成をサポート: 最大 4 つの主キー戦略 (分散固有 ID ジェネレーター - シーケンスを含む) をサポートし、主キーの問題を完全に解決するために自由に構成できます。

  • ActiveRecord モードのサポート: ActiveRecord フォーム呼び出しをサポートします。エンティティ クラスは Model クラスを継承するだけで強力な CRUD 操作を実行できます。

  • カスタムのグローバル一般操作をサポート: グローバル一般メソッド インジェクションをサポート (一度書いたらどこでも使用可能)

  • 組み込みのコード ジェネレーター: コードまたは Maven プラグインを使用して、マッパー、モデル、サービス、およびコントローラー層のコードを迅速に生成し、テンプレート エンジンをサポートし、使用を待機する多数のカスタム構成を用意します。

  • 組み込みのページング プラグイン: MyBatis の物理ページングに基づいており、開発者は特定の操作を気にする必要はありません。プラグインの設定後、ページングの記述は通常のリスト クエリと同等です。

  • ページング プラグインは複数のデータベースをサポートします。MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer およびその他のデータベースをサポートします。

  • 組み込みのパフォーマンス分析プラグイン: SQL ステートメントとその実行時間を出力できます。遅いクエリを迅速に発見できるため、開発およびテスト中にこの機能を有効にすることをお勧めします。

  • 組み込みのグローバル インターセプト プラグイン: テーブル全体の削除および更新操作のインテリジェントな分析とブロックを提供し、誤操作を防ぐためにインターセプト ルールをカスタマイズすることもできます。

3. サポートデータベース

MyBatisCRUDを使用でき、標準 SQL をサポートするデータベース。具体的なサポートは次のとおりです。以下のリストにない場合は、サポートについてチュートリアル PR のページネーション部分を確認してください。

  • MySQL、Oracle、DB2、H2、HSQL、SQLite、PostgreSQL、SQLServer、Phoenix、Gauss、ClickHouse、Sybase、OceanBase、Firebird、Cubrid、Goldilocks、csiidb

  • 大蒙データベース、徐谷データベース、人民大学金倉データベース、南達総合(華岳)データベース、南達総合データベース、神通データベース、漢高データベース

4. フレーム構造

まずエンティティクラスをスキャンし(エンティティスキャン)、リフレクション抽出によりエンティティクラスの属性を抽出(リフレクション抽出)し、操作対象のテーブルが誰であるか、操作対象のエンティティクラスの属性が誰であるかを分析します。フィールドは誰ですか. 次に、SQL ステートメントを生成し、それを mybati コンテナーに挿入します。操作するテーブルは、エンティティ クラスとエンティティ クラスの属性によって決定される必要があります。

5.正式な住所

公式サイト:MyBatis-Plus

公式ドキュメント:はじめに | MyBatis-Plus

2. 導入事例

1. 開発環境

  • イデア: イデア 2019.3.5

  • JDK:JDK8+

  • ビルドツール: Maven 3.5.4

  • MySQL:MySQL 8.0.24

  • ナビキャット:ナビキャットプレミアム15

  • スプリングブーツ:2.6.7

  • MyBatis-Plus:3.5.1

2. データベースとテーブルの構築

  • Navicat を開き、次の SQL スクリプトを実行してデータベースとテーブルを構築します。

    CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */; 
    use `mybatis_plus`; 
    CREATE TABLE `user` ( 
        `id` bigint(20) NOT NULL COMMENT '主键ID', 
        `name` varchar(30) DEFAULT NULL COMMENT '姓名', 
        `age` int(11) DEFAULT NULL COMMENT '年龄', 
        `email` varchar(50) DEFAULT NULL COMMENT '邮箱', 
        PRIMARY KEY (`id`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  • テストデータを挿入する

    INSERT INTO user (id, name, age, email) VALUES 
    (1, 'Jone', 18, '[email protected]'), 
    (2, 'Jack', 20, '[email protected]'), 
    (3, 'Tom', 28, '[email protected]'), 
    (4, 'Sandy', 21, '[email protected]'), 
    (5, 'Billie', 24, '[email protected]');

3. プロジェクトを作成する

  • Spring InitializerSpring Boot プロジェクトのクイック初期化を使用する

  • インポートされたMyBatis-Plus依存関係

    <!--mybatis-plus启动器-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.1</version>
    </dependency>
    ​
        <!-- 数据库驱动 -->
    <dependency>
        <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>8.0.27</version>
     </dependency>

  • 最も完全な jar 依存関係パッケージ 上記のスクリーンショット操作の最後のステップがチェックされていない場合は、次のすべての依存関係を prom ファイルに直接コピーできます。

    <dependencies>
    ​
        <!--web场景开发启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    ​
    ​
        <!--lombok:简化实体类开发-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    ​
        <!--开启测试启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    ​
        <!-- 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
    ​
        <!-- mybatis-plus启动器-->
        <!-- mybatis-plus 是自己开发,并非官方的! -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
    ​
        <!--多数据源依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.5.0</version>
        </dependency>
    ​
    </dependencies>

  • Lombokプラグインをインストールする

4. エンコーディングの設定

  • 設定application.yamlファイル

    spring:
      #配置数据库
      datasource:
        # 配置连接数据库信息
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
        username: root
        password: 123456
    ​
    mybatis-plus:
      # 配置MyBatis日志
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    ​
        #全局配置
      global-config:
        db-config:
          #配置统一的主键策略为自增,如果不设置默认为雪花算法
          id-type: auto
          # 设置实体类所对应的表的统一前缀,为实体类所对应的表名设置默认的前缀
          table-prefix: t_
    ​
      #配置mapper映射文件路径,名字要和mapper接口名字一致 这是默认路径不写也行
      mapper-locations: classpath*:/mapper/**/*.xml
    ​
      #配置类型别名所对应的包
      type-aliases-package: com.yka.boot02mybatis_plus.pojo
    ​
      # 扫描通用枚举的包
      type-enums-package: com.yka.boot02mybatis_plus.enums

  • 構成パッケージを作成して構成クラスを作成し、@MapperScan` アノテーションを追加し、Mapper フォルダーをスキャンして @Configuration アノテーションを追加します**

    @Configuration //告诉SpringBoot这是一个配置类 == spring配置文件
    @MapperScan("com.yka.boot02mybatis_plus.mapper")//扫描接口包 在当前配置类(spring.xml)中填写注解最合适
    //配置mapper接口的扫描配置
    //由mybatis-spring提供,可以将指定包下所有的mapper接口创建代理实现类
    //并将这些动态代理作为IOC容器的bean管理,接口就可以自动装配了,直接可以调用接口中的方法
    public class MyConfig {
    }

  • エンティティ クラスを作成しますUser.java(ここではコードを簡素化するために Lombok が使用されています)

    @Data//自动提供get set方法、tosString方法,equals方法
    @AllArgsConstructor//有参构造器
    @NoArgsConstructor//无参构造器
    @TableName("user")//绑定表 yaml文件中设置了全局配置这里可以不用注解了 yaml文件配置文件中:db-config:table-prefix: t_
    public class User {
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }

  • Mapperパッケージの下にUserMapperインターフェース

  • 1.BaseMapper<T>

    例証します:

    • 一般的な CRUD は BaseMapper インターフェイスをカプセル化し、Mybatis-Plus起動時、それをMybatis内部オブジェクト インジェクション コンテナーに変換します。

    • ジェネリックは任意のエンティティ オブジェクトTです

    • パラメータは、任意のタイプの主キーSerializableですMybatis-Plus。複合主キーの使用はお勧めできません。すべてのテーブルには独自の一意のid主。

    • オブジェクトは条件付き初期化子Wrapperです

    MyBatis-Plus の基本的な CRUD は組み込みの BaseMapper に実装されているため、このインターフェイスを継承した後、直接使用できます。

    今回説明した CRUD 操作には、条件付きコンストラクターをパラメーターとして持つメソッドは含まれていません。条件付きコンストラクターについては、別の章で説明します。BaseMapper で提供される CRUD メソッド:

  • // 在对应的Mapper上面继承基本的类 BaseMapper,就能直接用BaseMapper接口里面的sql语句了
    //MyBatis-Plus中的基本CRUD在内置的BaseMapper中都已得到了实现,因此我们继承该接口以后可以直接使用。
    @Repository // 代表持久层
    public interface UserMapper extends BaseMapper<User> {
    // 所有的CRUD操作都已经编写完成了
    // 你不需要像以前的配置一大堆文件了!
    ​
        @Select("select * from user where id = #{id}")
        public User selById(Integer id);
    ​
    }

5. テストクエリ

  • テストクラスを書くMyBatisPlusTest.java

    @SpringBootTest
    public class MyBatisPlusTest {
        @Resource
        private UserMapper userMapper;
    ​
        /**
         * 测试查询所有数据
         */
        @Test
        void testSelectList(){
            //通过条件构造器查询一个list集合,若没有条件,则可以设置null为参数
            List<User> users = userMapper.selectList(null);
            users.forEach(System.out::println);
        }
    }
  • コンソールにクエリ結果が出力されます

3、追加、削除、変更、確認

1.BaseMapper<T>

例証します:

  • 一般的な CRUD は BaseMapper インターフェイスをカプセル化し、Mybatis-Plus起動時、それをMybatis内部オブジェクト インジェクション コンテナーに変換します。

  • ジェネリックは任意のエンティティ オブジェクトTです

  • パラメータは、任意のタイプの主キーSerializableですMybatis-Plus。複合主キーの使用はお勧めできません。すべてのテーブルには独自の一意のid主。

  • オブジェクトは条件付き初期化子Wrapperです

MyBatis-Plus の基本的な CRUD は組み込みの BaseMapper に実装されているため、このインターフェイスを継承した後、直接使用できます。

今回説明した CRUD 操作には、条件付きコンストラクターをパラメーターとして持つメソッドは含まれていません。条件付きコンストラクターについては、別の章で説明します。


BaseMapper で提供される CRUD メソッド:

  • 追加: 挿入

    // 插入一条记录
    int insert(T entity);
  • 削除:削除

    // 根据 entity 条件,删除记录
    int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
    // 删除(根据ID 批量删除)
    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    // 根据 ID 删除
    int deleteById(Serializable id);
    // 根据 columnMap 条件,删除记录
    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
  • 変更: アップデート

    // 根据 whereWrapper 条件,更新记录
    int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
    // 根据 ID 修改
    int updateById(@Param(Constants.ENTITY) T entity);
  • クエリ: 選択

    // 根据 ID 查询
    T selectById(Serializable id);
    // 根据 entity 条件,查询一条记录
    T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    ​
    // 查询(根据ID 批量查询)
    List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    // 根据 entity 条件,查询全部记录
    List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    // 查询(根据 columnMap 条件)
    List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    // 根据 Wrapper 条件,查询全部记录
    List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    // 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
    List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    ​
    // 根据 entity 条件,查询全部记录(并翻页)
    IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    // 根据 Wrapper 条件,查询全部记录(并翻页)
    IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    // 根据 Wrapper 条件,查询总记录数
    Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

2. マッパー層を呼び出して CRUD を実装する

2.1 インサート


最終的な実行結果、取得したIDは1527206783590903810です。

これは、MyBatis-Plus がデータを挿入するときにデフォルトでスノーフレーク アルゴリズム戦略に基づいて ID を生成するためです。

/**
  * 测试插入一条数据 INSERT INTO user ( name, age, email ) VALUES ( ?, ?, ? )
  * MyBatis-Plus在实现插入数据时,会默认基于雪花算法的策略生成id,后面常用注解可以修改
  */
@Test
public void testInsert(){
    User user = new User();
    user.setName("Vz");
    user.setAge(21);
    user.setEmail("[email protected]");
    int result = userMapper.insert(user);
    System.out.println(result > 0 ? "添加成功!" : "添加失败!");
    System.out.println("受影响的行数为:" + result);
    //1527206783590903810(当前 id 为雪花算法自动生成的id)
    System.out.println("id自动获取" + user.getId());
}

2.2 削除


a. IDに応じたデータの削除

呼び出しメソッド: int deleteById(Serializable id);

/**
  * 测试根据id删除一条数据  DELETE FROM user WHERE id=?
  */
@Test
public void testDeleteById(){
    
    int result = userMapper.deleteById(1527206783590903810L);
    System.out.println(result > 0 ? "删除成功!" : "删除失败!");
    System.out.println("受影响的行数为:" + result);
}

b. IDに応じたデータの一括削除

呼び出しメソッド: int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

/**
  * 测试通过id批量删除数据  DELETE FROM user WHERE id IN ( ? , ? )
  */
@Test
public void testDeleteBatchIds(){
    List<Long> ids = Arrays.asList(6L,7L,8L);
    int result = userMapper.deleteBatchIds(ids);
    System.out.println(result > 0 ? "删除成功!" : "删除失败!");
    System.out.println("受影响的行数为:" + result);
}
//删除所有数据 DELETE FROM user 
userMapper.delete(null);

c. マップ条件に従ってデータを削除する

使用方法:int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

/**
   * 测试根据Map集合中所设置的条件删除数据  
   */
@Test
public void testDeleteByMap(){
    //当前演示为根据name和age删除数据
    //执行SQL为:DELETE FROM user WHERE name = ? AND age = ?
    Map<String,Object> map = new HashMap<>();
    map.put("name","Vz");
    map.put("age",21);
    int result = userMapper.deleteByMap(map);
    System.out.println(result > 0 ? "删除成功!" : "删除失败!");
    System.out.println("受影响的行数为:" + result);
}

2.3 変更点

呼び出しメソッド: int updateById(@Param(Constants.ENTITY) T エンティティ);

/**
  * 测试根据id修改用户信息
  */
@Test
public void testUpdateById(){
    //执行SQL为: UPDATE user SET name=?, age=?, email=? WHERE id=?
    User user = new User();
    user.setId(6L);
    user.setName("VzUpdate");
    user.setAge(18);
    user.setEmail("[email protected]");
    int result = userMapper.updateById(user);
    System.out.println(result > 0 ? "修改成功!" : "修改失败!");
    System.out.println("受影响的行数为:" + result);
}

2.4 クエリ


a. IDに基づいてユーザー情報を照会します

呼び出しメソッド: T selectById(Serializable id);

/**
  * 测试根据id查询用户数据 SELECT id,name,age,email FROM user WHERE id=?
  */
@Test
public void testSelectById(){
    User user = userMapper.selectById(1L);
    System.out.println(user);
}

b. 複数の ID に基づいて複数のユーザー情報を照会する

使用方法:List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

/**
  * 根据多个id查询用户数据
  */
@Test
public void testSelectBatchIds(){
    //执行SQL为:SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? )
    List<Long> ids = Arrays.asList(1L,2L,3L);
    List<User> users = userMapper.selectBatchIds(ids);
    users.forEach(System.out::println);
}

c. マップ条件に従ってユーザー情報を照会する

使用方法:List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

/**
  * 根据Map所设置的条件查询用户
  */
@Test
public void testSelectByMap(){
    //执行SQL为:SELECT id,name,age,email FROM user WHERE age = ?
    Map<String,Object> map = new HashMap<>();
    map.put("age",18);
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

d. すべてのユーザー情報を照会する

呼び出しメソッド: List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

/**
  * 测试查询所有数据  SELECT id,name,age,email FROM user
  */
@Test
void testSelectList(){
    List<User> users = userMapper.selectList(null);
    users.forEach(System.out::println);
}

3. 一般的なサービス

例証します:

  • 一般サービス CRUD カプセル化IServiceインターフェイス、さらなるカプセル化 CRUD はget 查询单行 remove 删除 list 查询集合 page 分页Mapper混乱を避けるためにレイヤーを区別するためにプレフィックス命名方法を使用します。

  • ジェネリックは任意のエンティティ オブジェクトTです

  • 一般的な Service メソッドをカスタマイズする可能性がある場合は、提供されているメソッドから継承したIBaseService独自の基本クラスを作成することをお勧めします。Mybatis-Plus

  • オブジェクトは条件付き初期化子Wrapperです

MyBatis-Plus には、一般的なビジネス層ロジックをカプセル化するインターフェイスIServiceServiceImpl。詳細については、ソース コード IService および ServiceImpl を参照してください。

したがって、使用する場合は、定義したインターフェイスでインターフェイスをService継承し、独自の実装クラスで独自の Service を実装し、それを継承するだけで済みますIServiceServiceImpl


IService の CRUD メソッド

  • 追加: 保存、SaveOrUpdate

    // 插入一条记录(选择字段,策略插入)
    boolean save(T entity);
    // 插入(批量)
    boolean saveBatch(Collection<T> entityList);
    // 插入(批量)
    boolean saveBatch(Collection<T> entityList, int batchSize);
    ​
    // TableId 注解存在更新记录,否插入一条记录
    //saveOrUpdate:会去表里查该id,如果表里有该id修改,无id是增加,新增不需要id,更改需要id
    boolean saveOrUpdate(T entity);
    // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
    boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
    // 批量修改插入 会去表里查该id,如果表里有该id修改,无id是增加,新增不需要id,更改需要id
    boolean saveOrUpdateBatch(Collection<T> entityList);
    // 批量修改插入 有id修改无id是增加
    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
  • 削除: 削除します

    // 根据 entity 条件,删除记录
    boolean remove(Wrapper<T> queryWrapper);
    // 根据 ID 删除
    boolean removeById(Serializable id);
    // 根据 columnMap 条件,删除记录
    boolean removeByMap(Map<String, Object> columnMap);
    // 删除(根据ID 批量删除)
    boolean removeByIds(Collection<? extends Serializable> idList);
  • 変更: アップデート

    // 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
    boolean update(Wrapper<T> updateWrapper);
    // 根据 whereWrapper 条件,更新记录
    boolean update(T updateEntity, Wrapper<T> whereWrapper);
    // 根据 ID 选择修改
    boolean updateById(T entity);
    // 根据ID 批量更新 有id修改无id是增加
    boolean updateBatchById(Collection<T> entityList);
    // 根据ID 批量更新 有id修改无id是增加
    boolean updateBatchById(Collection<T> entityList, int batchSize);
  • クエリ: 取得、リスト、カウント

    // 根据 ID 查询
    T getById(Serializable id);
    // 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
    T getOne(Wrapper<T> queryWrapper);
    // 根据 Wrapper,查询一条记录
    T getOne(Wrapper<T> queryWrapper, boolean throwEx);
    // 根据 Wrapper,查询一条记录
    Map<String, Object> getMap(Wrapper<T> queryWrapper);
    // 根据 Wrapper,查询一条记录
    <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
    ​
    ​
    // 查询所有
    List<T> list();
    // 查询列表
    List<T> list(Wrapper<T> queryWrapper);
    // 查询(根据ID 批量查询)
    Collection<T> listByIds(Collection<? extends Serializable> idList);
    // 查询(根据 columnMap 条件)
    Collection<T> listByMap(Map<String, Object> columnMap);
    // 查询所有列表
    List<Map<String, Object>> listMaps();
    // 查询列表
    List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
    // 查询全部记录
    List<Object> listObjs();
    // 查询全部记录
    <V> List<V> listObjs(Function<? super Object, V> mapper);
    // 根据 Wrapper 条件,查询全部记录
    List<Object> listObjs(Wrapper<T> queryWrapper);
    // 根据 Wrapper 条件,查询全部记录
    <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
    ​
    // 查询总记录数
    int count();
    // 根据 Wrapper 条件,查询总记录数
    int count(Wrapper<T> queryWrapper);
  • ページネーション: ページ

    default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {}

4.サービス層を呼び出してデータを操作する

Service インターフェースでは、MyBatis-Plus が提供する IService インターフェースを継承することで、MyBatis-Plus が提供する CRUD メソッドを取得できるだけでなく、独自に定義したメソッドを使用することもできます。

  • 創造UserServiceし継承するIService

    /**
      * UserService继承IService模板提供的基础功能 
      */
    public interface UserService extends IService<User> {}
  • 実装クラスを作成してUserService継承ServiceImpl

    /**
      * ServiceImpl实现了IService,提供了IService中基础功能的实现 
      * 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
      */
    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService{}
  • テストクエリレコードの数

    呼び出しメソッド: int count();

    @Test
    public void testGetCount(){
        //查询总记录数
        //执行的SQL为:SELECT COUNT( * ) FROM user
        long count = userService.count();
        System.out.println("总记录数:" + count);
    }

4、共通の注釈

MyBatis-Plus が提供するアノテーションは、データベースとエンティティ間のマッピングに関するいくつかの問題を解決するのに役立ちます。

1. @テーブル名

以上のテストの結果、MyBatis-Plus を使用して基本的な CRUD を実装する場合、操作対象のテーブルを指定せず、Mapper インターフェースが BaseMapper を継承するときに汎用の User を設定し、操作されるテーブルがユーザー テーブルであると結論付けられました。 MyBatis-Plus が操作するテーブルを決定するとき、それは BaseMapper のジェネリック型、つまりエンティティ タイプによって決定され、デフォルト操作のテーブル名はエンティティ タイプのクラス名と一致します。

1.1 問題の原因


エンティティクラス型のクラス名と操作対象のテーブルのテーブル名が一致しない場合、どのような問題が発生しますか?

  • userテーブルの名前を変更しt_userクエリ関数をテストします。

  • 現在のテーブル名、およびデフォルト操作のテーブル名がエンティティ タイプ、つまりテーブルのクラス名と一致しているため、プログラムは例外をスローします。テーブル'mybatis_plus.user' は存在しません。t_useruser

1.2 問題解決

a. 注釈を使用して問題を解決する

これをエンティティ クラス タイプに追加して、@TableName("t_user")エンティティ クラスに対応するテーブルを識別し、SQL ステートメントを正常に実行できるようになります。

@Data
@TableName("t_user")
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

b. グローバル構成を使用して問題を解決する

開発の過程で、私たちはしばしば上記の問題に遭遇します。つまり、エンティティ クラスに対応するテーブルには固定プレフィックスが付いています。t_たとえば、tbl_エンティティ クラスに対応する デフォルトのプレフィックスを設定すると、各エンティティ クラスの @TableName を通じてエンティティ クラスに対応するテーブルを識別する必要がなくなります

mybatis-plus:
  global-config:
    db-config:
      # 设置实体类所对应的表的统一前缀
      table-prefix: t_

2.@テーブルID

上記のテストの後、MyBatis-Plus が CRUD を実装すると、デフォルトで id を主キー列として使用し、データを挿入するときに、デフォルトでスノーフレーク アルゴリズム戦略に基づいて ID を生成します。

2.1 問題の原因


エンティティ クラスとテーブルが ID ではなく主キーを表し、uid などの他のフィールドを表す場合、MyBatis-Plus は自動的に uid を主キー列として認識しますか?

  • エンティティ クラスの属性idが変更されuid、テーブル内のフィールドも追加関数をテストするためにid変更されます。uid

  • プログラムは例外をスローします。フィールド 'uid' にはデフォルト値がありませんuid。これは、MyBatis-Plus が主キーとして値を割り当てていないことを示します。

2.2 問題解決


uid 属性をエンティティ クラスの主キーとして識別することで@TableId、SQL ステートメントを正常に実行できます。

@Date
public class User {
    //将属性所对应的字段指定为主键
    @TableId
    private Long uid;
    private String name;
    private Integer age;
    private String email;
}

2.3 @TableId の value 属性


エンティティクラスの主キーに対応する属性がid、テーブルの主キーを表すフィールドがuidの場合、このとき、属性idに@TableIdというアノテーションのみが付加されていると、例外として「Unknown columns」が発生します。 「フィールドリスト」の「id」がスローされます。つまり、MyBatis-Plus は引き続きテーブルの主キーとして id を使用し、テーブルの主キーはフィールド uid です。このとき、主キーを指定する必要があります。 @TableId アノテーションの value 属性を介したテーブルのキー フィールド、@TableId("uid")または@TableId(value="uid")

2.4 @TableId の type 属性


type 属性は、主キー戦略 (デフォルトのスノーフレーク アルゴリズム) を定義するために使用されます。

一般的に使用される主キー戦略:

価値 説明
IdType.ASSIGN_ID (デフォルト) データ ID は、データベース ID が自動インクリメントに設定されているかどうかに関係なく、スノーフレーク アルゴリズム戦略に基づいて生成されます。
IdType.AUTO データベースの自動インクリメント戦略を使用します。このタイプの場合、データベースが ID 自動インクリメントで設定されていることを確認してください。
    
//如果在.yaml文件中设置了全局配置这里就不用再写注解了
    //IdType.ASSIGN_ID(默认)基于雪花算法的策略生成数据id,与数据库id是否设置自增无关
    //@TableId(type = IdType.AUTO,value = "id")//使用数据库的自增策略,注意,该类型请确保数据库设置了id自增,
    // value:如果属性名和字段主键名不一致,可以指定主键字段
    private Long id;

グローバル主キー戦略を構成します。構成後、エンティティ クラスにアノテーションを記述する必要はありません。

#MyBatis-Plus相关配置
mybatis-plus:
  configuration:
    #配置日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      #配置统一的主键策略为自增
      id-type: auto
      # 设置实体类所对应的表的统一前缀
      table-prefix: t_

3.@TbaleField

上記のテストの後、MyBatis-Plus が SQL ステートメントを実行するときは、エンティティ クラスの属性名がテーブル内のフィールド名と一致していることを確認する必要があることがわかります。

エンティティクラスの属性名とフィールド名が一致しない場合はどうなりますか?

3.1 ケース 1


エンティティ クラスの属性がキャメル ケースの命名スタイルを使用し、テーブルのフィールドがアンダースコアの命名スタイルを使用する場合

たとえば、エンティティ クラスの属性userName、テーブル内のフィールドなどuser_name

この時点で、MyBatis-Plus はアンダースコア命名スタイルをハンプ命名スタイルに自動的に変換します。

MyBatisでの設定と同等

3.2 ケース 2


エンティティクラスの属性とテーブルのフィールドが条件1を満たさない場合

たとえば、エンティティ クラスの属性name、テーブル内のフィールドなどusername

このとき、エンティティクラス属性の@TableField("username")設定属性に対応するフィールド名を使用する必要があります。

public class User {
    @TableId("uid")
    private Long id;
    @TableField("username")
    private String name;
    private Integer age;
    private String email;
}

4.@テーブルロジック

4.1 墓石


物理的な削除: 実際の削除。対応するデータがデータベースから削除され、削除されたデータはその後クエリできなくなります。

論理的削除: 誤った削除。対応するデータ内で削除されたかどうかを示すフィールドのステータスを「削除済みステータス」に変更します。その後、このデータ レコードは引き続きデータベースに表示されます。

利用シーン:データ復旧可能

4.2 論理削除の実現


  • データベースに廃棄ステータス列を作成し、デフォルト値を 0 に設定します。0 は削除されず、1 は削除されました

  • エンティティクラスにトゥームストーン属性を追加

  • 削除機能をテストします。実際に実行されるのは変更です

    public void testDeleteById(){
        int result = userMapper.deleteById(1527472864163348482L);
        System.out.println(result > 0 ? "删除成功!" : "删除失败!");
        System.out.println("受影响的行数为:" + result);
    }

  • この時点でクエリ メソッドを実行すると、クエリの結果として自動的に条件が追加されます。is_deleted=0,查询的是未删除的数据

5. 条件コンストラクター

1. ラッパーの紹介

  • Wrapper: 条件付きで構築された抽象クラス、最上位の親クラス

  • AbstractWrapper: クエリ条件をカプセル化し、SQL の where 条件を生成するために使用されます。 

    • QueryWrapper: クエリ条件のカプセル化

    • UpdateWrapper: 条件付きパッケージを更新します

    • AbstractLambdaWrapper: Lambda 構文を使用する

      • LambdaQueryWrapper: Lambda 構文の使用のためのクエリ ラッパー

      • LambdaUpdateWrapper: Lambda 更新パッケージ ラッパー

2.クエリラッパー

  • クエリ条件の組み立て

    @Test
        public void test01(){
            //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
            //查询用户名包含a,年龄在20到30之间,邮箱信息不为null的用户信息
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("name","a").between("age",20,30).isNotNull("email");
            List<User> users = userMapper.selectList(queryWrapper);
            users.forEach(System.out::println);
        }

  • アセンブリのソート条件

     @Test
        public void test02(){
            //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,id ASC
            //查询用户信息,按照年龄的降序排序,若年龄相同,则按照id升序排序
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.orderByDesc("age").orderByAsc("id");
            List<User> users = userMapper.selectList(queryWrapper);
            users.forEach(System.out::println);
        }

  • アセンブリ削除条件

    実行SQL: UPDATE t_user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL)

    public void test03(){
        //删除邮箱地址为null的用户信息
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.isNull("email");
        int result = userMapper.delete(queryWrapper);
        System.out.println(result > 0 ? "删除成功!" : "删除失败!");
        System.out.println("受影响的行数为:" + result);
    }

  • 関数を変更する

    @Test
        public void test04(){
            //将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改
            //UPDATE t_user SET name=?, email=? WHERE is_deleted=0 AND (age > ? AND name LIKE ? OR email IS NULL)
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.gt("age",20).like("name","a")
                    .or()
                    .isNull("email");
            User user = new User();
            user.setName("小明");
            user.setEmail("[email protected]");
    ​
            int result = userMapper.update(user,queryWrapper);
            System.out.println(result > 0 ? "修改成功!" : "修改失败!");
            System.out.println("受影响的行数为:" + result);
        }

    条件の優先順位

     @Test
        public void test05(){
            //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
            //UPDATE t_user SET name=?, email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
            //lambda中优先执行,i就是条件构造器queryWrapper .and()和.or()都有lambda表达式
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("name","a")
                    .and(i-> i.gt("age",20).or().isNull("email"));
    ​
            User user = new User();
            user.setName("小红");
            user.setEmail("[email protected]");
    ​
            int result = userMapper.update(user, queryWrapper);
            System.out.println(result > 0 ? "修改成功!" : "修改失败!");
            System.out.println("受影响的行数为:" + result);
        }
     
  • select句の組み立て

    実行SQL: SELECT ユーザー名,年齢,メールアドレス FROM t_user WHERE is_deleted=0

    @Test
        public void test06(){
            //查询用户的用户名、年龄、邮箱信息
            //SELECT name,age,email FROM t_user WHERE is_deleted=0
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.select("name","age","email");
            List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
            maps.forEach(System.out::println);
        }

  • サブクエリを実装する

    @Test
        public void test07(){
            //查询id小于等于100的用户信息
            //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (id IN (select id from t_user          where id <= 100))
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.inSql("id", "select id from t_user where id <= 100");
            List<User> list = userMapper.selectList(queryWrapper);
            list.forEach(System.out::println);
        }

3.UpdateWrapper

UpdateWrapper は、QueryWrapper のアセンブリ条件機能だけでなく、該当条件のデータベース情報を変更するための set メソッドも提供します。

 @Test
    public void test08(){
        //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
        //UPDATE t_user SET name=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.like("name","a")
                .and( i -> i.gt("age",20).or().isNull("email"))
                .set("name","小黑").set("email","[email protected]");//设置修改的字段
​
        int result = userMapper.update(null, updateWrapper);
        System.out.println(result > 0 ? "修改成功!" : "修改失败!");
        System.out.println("受影响的行数为:" + result);
    }

4.条件パラメータ

条件パラメータの条件が満たされると、条件コンストラクタの条件が実行されます。

開発およびテスト中にクライアントからサーバーに渡されるパラメータをシミュレートします。

実際の開発プロセスでは、条件を組み立てるのは一般的な機能ですが、これらの条件データはユーザー入力によるものであり、オプションであるため、これらの条件を組み立てる際には、まずユーザーがこれらの条件を選択したかどうかを判断する必要があります。条件を組み立てます。選択がない場合 (null)、SQL の実行結果に影響を与えないように組み立ててはいけません。

  • アイデア 1複雑な文章

    実行SQL: SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (user_name LIKE ? AND age <= ?)

    //复杂写法,模拟开发中客户端传到服务器的参数就行测试
        @Test
        public void test09(){
            //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age <= ?)
            String username = "a";
            Integer ageBegin = null;
            Integer ageEnd = 30;
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            if(StringUtils.isNotBlank(username)){
                //isNotBlank判断某个字符创是否不为空字符串、不为null、不为空白符
                queryWrapper.like("name", username);
            }
            if(ageBegin != null){
                queryWrapper.ge("age", ageBegin);
            }
            if(ageEnd != null){
                queryWrapper.le("age", ageEnd);
            }
            List<User> list = userMapper.selectList(queryWrapper);
            list.forEach(System.out::println);
        }

  • アイデア 2簡単な書き方: 条件パラメーターを使用する

  • 条件パラメータの条件が満たされると、条件コンストラクタの条件が実行されます。

  • 上記の実装スキームに問題はありませんが、コードがより複雑になるため、オーバーロードされたメソッドと条件パラメータを使用してクエリ条件を構築し、コードの記述を簡素化できます。

     //简单写法:使用condition参数
        @Test
        public void test10(){
            //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age <= ?)
            String username = "a";
            Integer ageBegin = null;
            Integer ageEnd = 30;
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.like(StringUtils.isNotBlank(username),"name",username)
                    .ge(ageBegin!=null,"age",20)
                    .le(ageEnd!=null,"age",20);
            List<User> list = userMapper.selectList(queryWrapper);
          list.forEach(System.out::println);
        }

5.LambdaQueryWrapper

この関数は QueryWrapper と同等であり、間違った列名の入力を避けるためにラムダ式の構文が提供されています。フィールド名と属性名を自動的に照合する

 //LambdaQueryWrapper:提供了Lambda表达式的语法可以避免填错列名。通过属性名自动匹配字段名
    //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age <= ?)
    @Test
    public void test11(){
        String username = "a";
        Integer ageBegin = null;
        Integer ageEnd = 30;
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(username),User::getName,username)
                .ge(ageBegin!=null,User::getAge,20) //User::getAge:通过属性名自动匹配字段名
                .le(ageEnd!=null,User::getAge,20);
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

6.LambdaUpdateWrapper

この関数は UpdateWrapper と同等であり、間違った列名の入力を避けるためにラムダ式の構文が提供されています。

//LambdaUpdateWrapper:功能等同于UpdateWrapper,提供了Lambda表达式的语法可以避免填错列名。
//UPDATE t_user SET name=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
 @Test
public void test12(){
    //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
    LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper.like(User::getName, "a")
        .and(i -> i.gt(User::getAge, 20).or().isNull(User::getEmail));
    updateWrapper.set(User::getName, "小黑").set(User::getEmail,"[email protected]");
    int result = userMapper.update(null, updateWrapper);
    System.out.println("result:"+result);
}

よく使用される 6 つのプラグイン

1. ページネーションプラグイン

MyBatis Plusは独自のページングプラグインを備えており、簡単な設定でページング機能を実現できます。

  • mybatis-plus の依存関係をインポートする

    <!--mybatis-plus启动器-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.1</version>
    </dependency>
    ​
     <!-- 数据库驱动 -->
    <dependency>
        <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>8.0.27</version>
    </dependency>

  • 構成クラスの追加MyBatisPlusConfig

    @Configuration
    @MapperScan("com.atguigu.mybatisplus.mapper")
    public class MyBatisPlusConfig {
        //配置MybatisPlus的插件的  Interceptor:拦截器
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            //添加分页插件  DbType:数据库类型
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            return interceptor;
        }
    }
  • テストメソッドの書き込み

    @Test
    public void testPage(){
        //new Page()中的两个参数分别是当前页码,每页显示数量
        //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 LIMIT ?,?
            Page<User> page = new Page<>(2,2);
            Page<User> userPage = userMapper.selectPage(page, null);
    ​
            System.out.println(userPage);
          List<User> users = page.getRecords();//分页后的数据
            users.forEach(System.out::println);
    ​
            System.out.println("总页数:"+userPage.getPages());
            System.out.println("总条数:"+userPage.getTotal());
            System.out.println("当前页:"+userPage.getCurrent());
            System.out.println("当前页显示条数:"+userPage.getSize());
            System.out.println("是否有下一页:"+userPage.hasNext());
            System.out.println("是否有上一页:"+userPage.hasPrevious());
    }

2. カスタムページネーション

上記の呼び出しはMyBatis-Plusが提供するページング付きメソッドですが、自分で定義したメソッドにページングを実装するにはどうすればよいでしょうか?

  • インターフェースUserMapperでメソッドを定義する

    /**
      * 根据年龄大于20的用户查询用户列表,分页显示 
      * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位 
      * @param age 年龄 
      * @return 
      */
    Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);
  • UserMapper.xmlこのメソッドを実装する SQL を作成します

    <select id="selectPageVo" resultType="User">
        select id,username as name,age,email from t_user where age > #{age}
    </select>
  • テストメソッドの書き込み

    @Test
    public void testPageVo(){
        Page<User> page = userMapper.selectPageVo(new Page<User>(1,2), 20);
        List<User> users = page.getRecords();
        users.forEach(System.out::println);
    }

    2番目の書き方

    //自定义分页 根据年龄大于20的用户查询用户列表,分页显示 
    @Test
    public void testPageVo(){
        Page<User> page = new Page<>(1,2);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age",20);
        userMapper.selectPage(page,queryWrapper);
        List<User> users = page.getRecords();
        users.forEach(System.out::println);
    }

3. 楽観的ロック

機能: レコードが更新されるとき、このレコードが他の人によって更新されていないことが望まれます。

楽観的ロックの実装:

  • レコードを取得するときに、現在のバージョンを取得します

  • 更新する場合は、このバージョンを使用してください

  • 更新する場合は、UPDATE t_product SET name=?、price=100+50、version(変更後の新値+1)=1 WHERE id=? AND version(旧値)=0となります。

  • newVersion:oldVersion の条件を満たすと、毎回古い値が +1 されます。

  • バージョンが間違っているとアップデートに失敗します

3.1 シナリオ


  • 商品の原価は 80 元、販売価格は 100 元です。上司はまずシャオ・リーに、製品の価格を50元値上げするように通知しました。シャオ・リーはゲームをしていて1時間遅れた。ちょうど 1 時間後、上司は商品の価格が 150 元に値上がりしており、高すぎて売り上げに影響するかもしれないと感じました。また、Xiao Wang に商品の価格を 30 元値下げすることを伝えます。

  • 現時点では、Xiao Li と Xiao Wang が商品バックグラウンド システムを同時に操作します。Xiao Liが動作するとき、システムは最初に製品価格100元を取り出しましたが、Xiao Wangも動作しており、取り出した製品の価格も100元でした。Xiao Liは価格に50元追加して100+50=150元をデータベースに保存し、Xiao Wangは製品を30元値下げして100-30=70元をデータベースに保存しました。はい、ロックがない場合、Xiao Li の操作は Xiao Wang の操作によって完全にカバーされます。

  • 現在の商品価格は70元で、原価より10元安い。数分後、この商品はすぐに 1,000 点以上の商品が売れ、上司は 10,000 点以上を失いました。

3.2 楽観的ロックと悲観的ロック


  • 上記のストーリーでは、楽観的ロックの場合、Xiao Wang は価格を保存する前に価格が変更されているかどうかを確認します。変更されている場合は、変更後の価格 150 元が再度取得され、データベースに 120 元が保存されます。

  • 悲観的ロックの場合、シャオ・リーがデータを取り出した後、シャオ・ワンはシャオ・リーが操作を完了した後の価格でのみ操作でき、最終価格は120元が保証されます。

3.3 変更の競合をシミュレートする


  • 商品テーブルをデータベースに追加

    CREATE TABLE t_product ( 
        id BIGINT(20) NOT NULL COMMENT '主键ID', 
        NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称', 
        price INT(11) DEFAULT 0 COMMENT '价格', 
        VERSION INT(11) DEFAULT 0 COMMENT '乐观锁版本号', 
        PRIMARY KEY (id) 
    );

  • データを追加する

    INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本', 100);

  • エンティティクラスを追加するProduct

    @Data
    public class Product {
        private Long id;
        private String name;
        private Integer price;
        private Integer version;
    }

  • マッパーインターフェイスを追加するProductMapper

    public interface ProductMapper extends BaseMapper<Product> {}

  • 試験方法

      
      //模拟修改冲突
        @Test
        public void testProduct01(){
            //1.小李获取商品价格
            //SELECT id,name,price,version FROM t_product WHERE id=?
            Product productLi = productMapper.selectById(1);
            System.out.println("小李获取的商品价格为:" + productLi.getPrice());//100
    ​
            //2.小王获取商品价格
            //SELECT id,name,price,version FROM t_product WHERE id=?
            Product productWang = productMapper.selectById(1);
            System.out.println("小王获取的商品价格为:" + productWang.getPrice());//100
    ​
            //3.小李修改商品价格+50
            productLi.setPrice(productLi.getPrice()+50);
            //UPDATE t_product SET name=?, price=100+50, version=? WHERE id=?
            productMapper.updateById(productLi);//100+50=150
    ​
            //4.小王修改商品价格-30
            productWang.setPrice(productWang.getPrice()-30);
            //UPDATE t_product SET name=?, price=100-30, version=? WHERE id=?
            productMapper.updateById(productWang);//100-30=70
    ​
            //5.老板查询商品价格
            //SELECT id,name,price,version FROM t_product WHERE id=?
            Product productBoss = productMapper.selectById(1);
            System.out.println("老板获取的商品价格为:" + productBoss.getPrice());//70
        }

  • の結果

3.4 楽観的ロックが問題を解決する


  • エンティティクラスのフィールドversionにアノテーションを追加する@Version

    @Data
    public class Product {
        private Long id;
        private String name;
        private Integer price;
        @Version //标识乐观锁版本号字段
        private Integer version;
    }

  • オプティミスティックロックプラグイン設定を追加

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }

  • テストメソッドを再実行、オプティミスティックロックテスト

    Xiao Li は製品情報について次のように問い合わせています。

    SELECT id,name,price,version FROM t_product WHERE id=?

    Xiao Wang は製品情報について次のように問い合わせています。

    SELECT id,name,price,version FROM t_product WHERE id=?

    Xiao Li は製品価格を変更し、バージョン + 1 を自動的に追加します

    UPDATE t_product SET 名前=?、価格=?、バージョン=? ここでid=? かつバージョン=?

    パラメータ: 外国人ノートブック (文字列)、150 (整数)、1 (整数)、1 (長整数)、0 (整数)

    Xiao Wang は製品の価格を変更します。この時点でバージョンは更新されています。条件が満たされない場合、変更は失敗します。

    UPDATE t_product SET 名前=?、価格=?、バージョン=? ここでid=? かつバージョン=?

    パラメータ: 外国人ノートブック (文字列)、70 (整数)、1 (整数)、1 (長整数)、0 (整数)

    結局、Xiao Wang は変更に失敗し、クエリ価格: 150

    SELECT id,name,price,version FROM t_product WHERE id=?

  • //乐观锁测试
    @Test
    public void testProduct02(){
     //1.小李获取商品价格
     //SELECT id,name,price,version FROM t_product WHERE id=?
     Product productLi = productMapper.selectById(1);
     System.out.println("小李获取的商品价格为:" + productLi.getPrice());//100
    ​
     //2.小王获取商品价格
     //SELECT id,name,price,version FROM t_product WHERE id=?
     Product productWang = productMapper.selectById(1);
     System.out.println("小王获取的商品价格为:" + productWang.getPrice());//100
    ​
     //3.小李修改商品价格+50
     productLi.setPrice(productLi.getPrice()+50);//100+50=150
     //UPDATE t_product SET name=?, price=100+50, version=1 WHERE id=? AND version=0
     productMapper.updateById(productLi);//现在版本号已经变成1了
    ​
     //4.小王修改商品价格-30
     productWang.setPrice(productWang.getPrice()-30); //100-30=70
     //UPDATE t_product SET name=?, price=100-30, version=1 WHERE id=? AND version=0
     productMapper.updateById(productWang);//现在版本号已经变成1了,所以这里没有修改成功
    ​
     //5.老板查询商品价格
     //SELECT id,name,price,version FROM t_product WHERE id=?
     Product productBoss = productMapper.selectById(1);
     System.out.println("老板获取的商品价格为:" + productBoss.getPrice());//150
    }

  • 実行プロセスを最適化する

      //优化乐观锁测试
        @Test
        public void testProduct03(){
            //1.小李获取商品价格
            //SELECT id,name,price,version FROM t_product WHERE id=?
            Product productLi = productMapper.selectById(1);
            System.out.println("小李获取的商品价格为:" + productLi.getPrice());//100
    ​
            //2.小王获取商品价格
            //SELECT id,name,price,version FROM t_product WHERE id=?
            Product productWang = productMapper.selectById(1);
            System.out.println("小王获取的商品价格为:" + productWang.getPrice());//100
    ​
            //3.小李修改商品价格+50
            productLi.setPrice(productLi.getPrice()+50);//100+50=150
            //UPDATE t_product SET name=?, price=100+50, version=1 WHERE id=? AND version=0
            productMapper.updateById(productLi);//现在版本号已经变成1了
    ​
            //4.小王修改商品价格-30
            productWang.setPrice(productWang.getPrice()-30); //100-30=70
            //UPDATE t_product SET name=?, price=100-30, version=1 WHERE id=? AND version=0
            int result = productMapper.updateById(productWang);//现在版本号已经变成1了,所以这里没有修改成功
            if(result == 0){
                //操作失败,重试
                //重新查询一次,这是小李修改玩的数据
                Product productNew = productMapper.selectById(1);
                //再次更新,现在价格是150-30
                productNew.setPrice(productNew.getPrice()-30);
                //再次修改
              productMapper.updateById(productNew);
            }
    ​
            //5.老板查询商品价格
            //SELECT id,name,price,version FROM t_product WHERE id=?
            Product productBoss = productMapper.selectById(1);
            System.out.println("老板获取的商品价格为:" + productBoss.getPrice());//120
        }

七、一般列挙

テーブル内の一部のフィールド値は、性別 (男性または女性) など、固定されています。現時点では、MyBatis-Plus の一般的な列挙を使用して、

  • データベーステーブルにフィールドを追加sex

  • 汎用列挙型を作成する

    @Getter
    public enum SexEnum {
        MALE(1, "男"),
        FEMALE(2, "女");
    ​
        @EnumValue //将注解所标识的属性的值存储到数据库中
        private int sex;
        private String sexName;
    ​
        SexEnum(Integer sex, String sexName) {
            this.sex = sex;
            this.sexName = sexName;
        }
    }

  • ユーザーエンティティクラスに性別属性を追加

    public class User {
        private Long id;
        @TableField("username")
        private String name;
        private Integer age;
        private String email;
    ​
        @TableLogic
        private int isDeleted;  //逻辑删除
    ​
        private SexEnum sex;
    }

  • スキャンの汎用列挙の構成

    #MyBatis-Plus相关配置
    mybatis-plus:
      #指定mapper文件所在的地址
      mapper-locations: classpath:mapper/*.xml
      configuration:
        #配置日志
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      global-config:
        banner: off
        db-config:
          #配置mp的主键策略为自增
          id-type: auto
          # 设置实体类所对应的表的统一前缀
          table-prefix: t_
      #配置类型别名所对应的包
      type-aliases-package: com.atguigu.mybatisplus.pojo
      # 扫描通用枚举的包
      type-enums-package: com.atguigu.mybatisplus.enums

  • テストメソッドを実行する

    @Test
    public void test(){
        User user = new User();
        user.setName("admin");
        user.setAge(33);
        user.setSex(SexEnum.MALE);
        int result = userMapper.insert(user);
        System.out.println("result:"+result);
    }

8. コードジェネレーター

1. 依存関係を導入する

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>//代码生成器的核心依赖
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>//freemarker引擎模板依赖
    <version>2.3.31</version>
</dependency>

2.高速生成

public class FastAutoGeneratorTest {
​
  public static void main(String[] args) {
    FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mybatis_plus?
         characterEncoding=utf-8&userSSL=false", "root", "123456")
    .globalConfig(builder -> {
        builder.author("atguigu") // 设置作者
         //.enableSwagger() // 开启 swagger 模式
        .fileOverride() // 覆盖已生成文件
        .outputDir("D://mybatis_plus"); // 指定输出目录
     })
    .packageConfig(builder -> {
        builder.parent("com.atguigu") // 设置父包名
        .moduleName("mybatisplus") // 设置父包模块名
         // 设置mapperXml映射文件生成路径
        .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus"));
    })
    .strategyConfig(builder -> {
        builder.addInclude("t_user") // 设置需要生成的表名
        .addTablePrefix("t_", "c_"); // 设置过滤表前缀
    })
    .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
    .execute();
    }
}

9 つの複数のデータ ソース

さまざまなシナリオに適用可能: 純粋なマルチライブラリ、読み取りと書き込みの分離、1 つのマスターと複数のスレーブ、混合モードなど。

シナリオの説明:

mybatis_plus(以前のライブラリは移動しません) と(新規) の2 つのライブラリをそれぞれ作成し、 mybatis_plus_1mybatis_plus ライブラリのproductテーブルを mybatis_plus_1 ライブラリに移動します。これにより、各ライブラリがテーブルを持ち、ユーザー データと製品データが取得されます。テスト ケースを通じて取得された場合、マルチライブラリ シミュレーションが成功したことを意味します。

1. データベースとテーブルを作成する

  • データベースmybatis_plus_1とテーブル「product」を作成する

    CREATE DATABASE `mybatis_plus_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
    use `mybatis_plus_1`; 
    CREATE TABLE product ( 
        id BIGINT(20) NOT NULL COMMENT '主键ID', 
        name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称', 
        price INT(11) DEFAULT 0 COMMENT '价格', 
        version INT(11) DEFAULT 0 COMMENT '乐观锁版本号', 
        PRIMARY KEY (id) 
    );
  • テストデータの追加

    INSERT INTO product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
  • テーブルをmybatis_plusライブラリにドロップするproduct

    use mybatis_plus; 
    DROP TABLE IF EXISTS product;

2. 新しいプロジェクトで依存関係が導入される

新しい Spring Boot プロジェクトを自分で作成し、MySQL ドライバーと Lombok 依存関係を選択します。

複数のデータソースへの依存関係を導入する

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.0</version>
</dependency>

3. 設定ファイルを書き込む

spring:
  # 配置数据源信息
  datasource:
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: master
      # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: false
       # 配置多数据源信息
      datasource:
      #主数据源
        master:
          url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: 123456
        #从数据源
        slave_1:
          url: jdbc:mysql://localhost:3306/mybatis_plus_1?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: 123456

4. エンティティクラスの作成

  • 新しいUserエンティティ クラスを作成します (データベース テーブル名に t_ プレフィックスが付いている場合は、忘れずに構成してください)

    @Data
    @TableName("t_user")//指定表
    public class User {
    ​
        private Long id;
    ​
        private String name;
    ​
        private Integer age;
    ​
        private Integer sex;
    ​
        private String email;
    ​
        private Integer isDeleted;
    }

  • 新しいエンティティクラスを作成するProduct

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Product {
    ​
        private Long id;
    ​
        private String name;
    ​
        private Integer price;
    ​
        private Integer version;
    }

5. マッパーとサービスの作成

  • 新しいインターフェースUserMapper

    @Repository
    public interface UserMapper extends BaseMapper<User> {}
  • 新しいインターフェースProductMapper

    @Repository
    public interface ProductMapper extends BaseMapper<Product> {}
  • 新しいサービス インターフェイスを作成してUserService、操作のデータ ソースを指定します

    public interface UserService extends IService<User> {}
  • 新しいサービス インターフェイスを作成してProductService、操作のデータ ソースを指定します

    public interface ProductService extends IService<Product> {}
  • 作成したUserService実装クラス

    @Service
    @DS("master")//多数据源操作,指定要操作的数据源,master指定的是mybatis_plus数据库里面有t_user表,不写注解使用默认数据源
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    }

    作成したProductService実装クラス

    @Service
    @DS("slave_1")//多数据源操作,指定要操作的数据源,slave_1指定的是mybatis_plus1数据库里面有product表,不写注解使用默认数据源
    public class ProductServiceImpl extends ServiceImpl<ProductMapper,Product> implements ProductService{
    ​
    }

6. テストメソッドを書く

スタートアップ クラスに注釈を忘れずに追加してください@MapperScan()

class TestDatasourceApplicationTests {
    @Resource
    UserService userService;
​
    @Resource
    ProductService productService;
​
    @Test
    void contextLoads() {
        User user = userService.getById(1L);
        Product product = productService.getById(1L);
        System.out.println("User = " + user);
        System.out.println("Product = " + product);
    }
​
}

10.MyBatisXプラグイン

MyBatis-Plus は、開発効率を大幅に向上させる強力なマッパーとサービス テンプレートを提供します。

しかし、実際の開発プロセスでは、複雑な SQL や複数テーブルの結合クエリなど、MyBatis-Plus ですべての問題を解決できるわけではありません。コードと SQL ステートメントを自分で記述する必要があります。この問題を迅速に解決するにはどうすればよいでしょうか。今度は、MyBatisX プラグインを使用できます。

MyBatisX は、効率性を追求して生まれた、IDEA ベースの迅速な開発プラグインです。

1.MyBatisXプラグインをインストールします

IDEA を開き、ファイル -> 設定 -> プラグイン -> MyBatisX を開き、検索バーで MyBatisX を検索してインストールします。

2. コードを迅速に生成する

  • 新しい Spring Boot プロジェクトを作成して依存関係を導入します (プロジェクトの作成時に Lombok ドライバーと mysql ドライバーを忘れずに確認してください)

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.1</version>
    </dependency>
    ​
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        <version>3.5.0</version>
    </dependency>

  • データソース情報の構成

    spring:
      #配置数据库
      datasource:
        # 配置连接数据库信息
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
        username: root
        password: 123456

  • IDEA でデータベースとのリンクを確立する

  • データベース情報を入力して保存します

  • ?characterEncoding=utf8&serverTimezone=アジア/上海&useSSL=false?characterEncoding=utf8&serverTimezone=アジア/上海&useSSL=false 

  • 生成する必要があるテーブルを見つけて右クリックします

  • 情報を入力したら次のステップへ

  • 情報を入力し続けます

  • これで完了です (yyds の使い方は本当に簡単です)

3. CRUDを迅速に生成する

MyBaitsX は、Mapper インターフェイスに入力したメソッド名に従って、対応する SQL ステートメントを生成するのに迅速に役立ちます。

public interface UserMapper extends BaseMapper<User> {
​
    //mybatisX快速生成CRUD 方法名都是见名实意
    //增加
    int insertSelective(User user);
​
    //删除 通过id和年龄和姓名删除
    int deleteByIdAndAgeAndName(@Param("id") Long id, @Param("age") Integer age, @Param("name") String name);
​
    //修改 通过id修改年龄跟性别
    int updateAgeAndSexById(@Param("age") Integer age, @Param("sex") Integer sex, @Param("id") Long id);
​
    //查询 通过开始年龄和结束年龄查询年龄和性别
    List<User> selectAgeAndSexByAgeBetween(@Param("beginAge") Integer beginAge, @Param("endAge") Integer endAge);
​
    //查询全部
    List<User> selectAll();
​
    //通过年龄降序查询全部
    List<User> selectAllOrderByAgeDesc();
}
​

テスト

@SpringBootTest
class Boot04MybatisxDemoApplicationTests {
​
    @Autowired
    UserMapper userMapper;
​
    @Test
    void contextLoads() {
        
        List<User> users = userMapper.selectAll();
        System.out.println(users);
        
        List<User> users1 = userMapper.selectAgeAndSexByAgeBetween(10, 20);
        System.out.println(users1);
    }
​
}

おすすめ

転載: blog.csdn.net/m0_65992672/article/details/130565444