(メッセージ数 5 件) mybatis-plus の使い方 (1) - スラグベイビーエンジニアのブログ - CSDN ブログ
ARモード
ActiveRecord モードは、エンティティ オブジェクトを操作することでデータベース テーブルを直接操作します。ORM に似ています。
例は次のとおりです
-
エンティティクラスを
User
継承させるModel
package com.example.mp.po;
import com.baomidou.mybatisplus.annotation.SqlCondition;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
@EqualsAndHashCode(callSuper = false)
@Data
public class User extends Model<User> {
private Long id;
@TableField(condition = SqlCondition.LIKE)
private String name;
@TableField(condition = "%s > #{%s}")
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
}
-
エンティティオブジェクトのメソッドを直接呼び出す
@Test
public void insertAr() {
User user = new User();
user.setId(15L);
user.setName("我是AR猪");
user.setAge(1);
user.setEmail("[email protected]");
user.setManagerId(1L);
boolean success = user.insert(); // 插入
System.out.println(success);
}
-
結果
他の例
// 查询
@Test
public void selectAr() {
User user = new User();
user.setId(15L);
User result = user.selectById();
System.out.println(result);
}
// 更新
@Test
public void updateAr() {
User user = new User();
user.setId(15L);
user.setName("王全蛋");
user.updateById();
}
//删除
@Test
public void deleteAr() {
User user = new User();
user.setId(15L);
user.deleteById();
}
主キー戦略
エンティティ クラスを定義するときは、@TableId
指定された主キーを使用します。そのtype
属性によって主キー戦略を指定できます。
mp は複数の主キー戦略をサポートしており、デフォルトの戦略はスノーフレーク アルゴリズムに基づく自己インクリメント ID です。IdType
すべての主キー戦略は、IdType
次の値を使用して列挙型クラスで定義されます。
-
AUTO
データベース ID はデータベースに応じて自動的に増加します。挿入操作で SQL ステートメントが生成される場合、主キーの列は挿入されません。
-
NONE
主キーのタイプが設定されていません。主キーがコード内で手動で設定されていない場合、主キーのグローバル ポリシーに従って自動的に生成されます(主キーのデフォルトのグローバル ポリシーは、スノーフレーク アルゴリズムの自己インクリメント ID に基づいています)。
-
INPUT
主キーが設定されていない場合は、手動で設定する必要があります。挿入操作によって SQL ステートメントが生成されると、主キー列の値は になります
null
。Oracle のシリアル主キーはこのメソッドを使用する必要があります -
ASSIGN_ID
主キーが手動で設定されていない場合、つまりエンティティ クラスの主キー属性が空の場合、スノーフレーク アルゴリズムを使用して自動的に設定されます。
-
ASSIGN_UUID
エンティティ クラスの主キー属性が空の場合、UUID を使用して自動的に設定されます。
-
.... (古いものもまだいくつかあるので、これ以上リストしません)
エンティティ クラスごとに、アノテーションを使用してエンティティ クラスの主キー ストラテジを指定できます。これは、ローカル ストラテジ@TableId
として理解できます。すべてのエンティティクラスに同じ主キー戦略を採用したい場合、各エンティティクラスを一つずつ設定するのは大変なので、このとき主キーのグローバル戦略を使用できます。設定するだけです。たとえば、グローバル自動インクリメント主キー戦略を構成します。application.yml
# application.yml
mybatis-plus:
global-config:
db-config:
id-type: auto
以下は、さまざまな主キー戦略の動作を示しています。
-
AUTO
User
上記の属性に注釈を付けてから、id
MYSQL テーブルのuser
主キーを自動インクリメントするように変更します。
@EqualsAndHashCode(callSuper = false)
@Data
public class User extends Model<User> {
@TableId(type = IdType.AUTO)
private Long id;
@TableField(condition = SqlCondition.LIKE)
private String name;
@TableField(condition = "%s > #{%s}")
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
}
テスト
@Test
public void testAuto() {
User user = new User();
user.setName("我是青蛙呱呱");
user.setAge(99);
user.setEmail("[email protected]");
user.setCreateTime(LocalDateTime.now());
userMapper.insert(user);
System.out.println(user.getId());
}
結果
主キー ID がコードに設定されておらず、発行された SQL ステートメントにも主キー ID が設定されていないことがわかります。挿入が完了すると、主キー ID がエンティティ オブジェクトに書き戻されます。
-
NONE
MYSQL
user
テーブルで、主キーの自動インクリメントを削除します。次に、User
クラスを変更します (@TableId
アノテーションが構成されていない場合、デフォルトの主キー戦略も ですNONE
)。
@TableId(type = IdType.NONE)
private Long id;
挿入時に、エンティティ クラスの主キー ID に値がある場合はそれを使用し、主キー ID が空の場合は主キー グローバル戦略を使用して ID を生成します。
-
残りの戦略も同様であるため、繰り返しません。
まとめ
AUTO
データベースの自動インクリメント主キーに依存するため、挿入時にエンティティ オブジェクトは主キーを設定する必要がありません。挿入が成功すると、主キーがエンティティ オブジェクトに書き戻されます。
INPUT はユーザー入力に完全に依存します。エンティティ オブジェクトの主キー ID と、データベースに挿入されるときに設定されるもの。値がある場合は値を設定し、null の場合は null を設定します
残りの戦略は、エンティティ オブジェクトの主キー ID が空の場合に自動的に生成されます。
NONE
世界戦略に従い、ASSIGN_ID
スノーフレークアルゴリズムを採用し、ASSIGN_UUID
UUIDを使用します
グローバル設定の場合は でapplication.yml
実行でき、単一のエンティティ クラスのローカル設定の場合は@TableId
それを使用できます。エンティティ クラスの場合、ローカル主キー戦略がある場合はそれが採用され、そうでない場合はグローバル戦略に従います。
構成
application.yml
mybatis plusには、上記のグローバル主キー戦略など、多くの設定可能な項目があります。いくつかの設定項目を以下に示します。
基本構成
-
configLocation
: 別の mybatis 設定がある場合、このアノテーションを使用して mybatis の設定ファイル (mybatis のグローバル設定ファイル) を指定します。 -
mapperLocations
: mybatisマッパーに対応するxmlファイルの場所 -
typeAliasesPackage
: mybatis エイリアス パッケージのスキャン パス -
……
高度な構成
-
mapUnderscoreToCamelCase
: 自動キャメルケース命名規則マッピングを有効にするかどうか。(デフォルトで有効) -
dbTpe
: データベースの種類。通常、構成は必要なく、データベース接続 URL に従って自動的に識別されます。 -
fieldStrategy
: (廃止) フィールド検証戦略。この構成項目は、最新バージョンの mp ドキュメントには存在しなくなり、insertStrategy
、updateStrategy
、に細分化されていますselectStrategy
。デフォルト値は ですNOT_NULL
。つまり、エンティティ オブジェクト内の null 以外のフィールドのみが最終的な SQL ステートメントにアセンブルされます。
次のようないくつかのオプション構成があります。インタビューブック: https://www.yoodb.com
この構成アイテムは、でグローバルに構成することapplication.yml
も、エンティティ クラスのフィールドの注釈を使用してローカルに構成することもできます。@TableField
このフィールド検証戦略は何に役立ちますか? これは UPDATE 操作に反映できます。User
オブジェクトを使用して UPDATE 操作を実行する場合、User
オブジェクト内の null 以外の属性のみがデータベースで更新され、他の属性は更新されないことが期待さNOT_NULL
れます。要件。
updateStrategy
として設定すると、IGNORED
非 null 判定は行われず、エンティティ オブジェクト内のすべての属性が忠実に SQL に組み立てられます。このように、UPDATE を実行すると、更新したくないフィールドが表示される場合があります。に設定されますNULL
。
-
IGNORED
: 検証を無視します。つまり、検証は行われません。エンティティ オブジェクト内のすべてのフィールドは、値に関係なく、SQL ステートメントに忠実にアセンブルされます (最初のNULL
フィールドは SQL ステートメントにアセンブルされますNULL
)。 -
NOT_NULL
:未NULL
検証。非フィールドのみがNULL
SQL ステートメントに組み込まれます。 -
NOT_EMPTY
: null 以外のチェックサム。フィールドが文字列型の場合、空ではない文字列のみがアセンブルされます。他の型のフィールドの場合は、次と同等です。NOT_NULL
-
NEVER
: SQL を結合しません。すべてのフィールドが SQL ステートメントに追加されるわけではありません -
tablePrefix
: テーブル名のプレフィックスを追加します
例えば
mybatis-plus:
global-config:
db-config:
table-prefix: xx_
次に、MYSQL でテーブルを変更します。ただし、Java エンティティ クラスは同じままです (依然としてUser
)。
テスト
@Test
public void test3() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name", "黄");
Integer count = userMapper.selectCount(wrapper);
System.out.println(count);
}
テーブル名の前にプレフィックスを追加して、結合された SQL が表示されます。
コードジェネレーター
mp は、エンティティ エンティティ クラス、マッパー インターフェイス、サービス、コントローラーなどの完全なコード セットを迅速に生成できるジェネレーターを提供します。
例は次のとおりです
public class GeneratorTest {
@Test
public void generate() {
AutoGenerator generator = new AutoGenerator();
// 全局配置
GlobalConfig config = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
// 设置输出到的目录
config.setOutputDir(projectPath + "/src/main/java");
config.setAuthor("yogurt");
// 生成结束后是否打开文件夹
config.setOpen(false);
// 全局配置添加到 generator 上
generator.setGlobalConfig(config);
// 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/yogurt?serverTimezone=Asia/Shanghai");
dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
dataSourceConfig.setUsername("root");
dataSourceConfig.setPassword("root");
// 数据源配置添加到 generator
generator.setDataSource(dataSourceConfig);
// 包配置, 生成的代码放在哪个包下
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.example.mp.generator");
// 包配置添加到 generator
generator.setPackageInfo(packageConfig);
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
// 下划线驼峰命名转换
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
// 开启lombok
strategyConfig.setEntityLombokModel(true);
// 开启RestController
strategyConfig.setRestControllerStyle(true);
generator.setStrategy(strategyConfig);
generator.setTemplateEngine(new FreemarkerTemplateEngine());
// 开始生成
generator.execute();
}
}
実行後、以下の図に示すように、完全なコード セットが生成されることがわかります。
高度な機能
高度な機能のデモンストレーションには新しいテーブルが必要ですuser2
DROP TABLE IF EXISTS user2;
CREATE TABLE user2 (
id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键id',
name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
age INT(11) DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
manager_id BIGINT(20) DEFAULT NULL COMMENT '直属上级id',
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
update_time DATETIME DEFAULT NULL COMMENT '修改时间',
version INT(11) DEFAULT '1' COMMENT '版本',
deleted INT(1) DEFAULT '0' COMMENT '逻辑删除标识,0-未删除,1-已删除',
CONSTRAINT manager_fk FOREIGN KEY(manager_id) REFERENCES user2(id)
) ENGINE = INNODB CHARSET=UTF8;
INSERT INTO user2(id, name, age, email, manager_id, create_time)
VALUES
(1, '老板', 40 ,'[email protected]' ,NULL, '2021-03-28 13:12:40'),
(2, '王狗蛋', 40 ,'[email protected]' ,1, '2021-03-28 13:12:40'),
(3, '王鸡蛋', 40 ,'[email protected]' ,2, '2021-03-28 13:12:40'),
(4, '王鸭蛋', 40 ,'[email protected]' ,2, '2021-03-28 13:12:40'),
(5, '王猪蛋', 40 ,'[email protected]' ,2, '2021-03-28 13:12:40'),
(6, '王软蛋', 40 ,'[email protected]' ,2, '2021-03-28 13:12:40'),
(7, '王铁蛋', 40 ,'[email protected]' ,2, '2021-03-28 13:12:40')
そして、対応するエンティティクラスを作成しますUser2
package com.example.mp.po;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class User2 {
private Long id;
private String name;
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private Integer version;
private Integer deleted;
}
そしてマッパーインターフェース
package com.example.mp.mappers;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mp.po.User2;
public interface User2Mapper extends BaseMapper<User2> { }
墓石
そもそも、なぜ墓石があるのでしょうか?そのまま削除できないんですか?もちろん可能ですが、これらのデータを復元したい場合、または将来的に表示する必要がある場合は、それを行うことはできません。論理削除は、データの回復を容易にし、データ自体の価値を保護するためのソリューションです。
日常生活では、コンピューター内のファイルを削除した後、そのファイルをごみ箱に入れるだけで、後で必要に応じて表示したり復元したりできます。ファイルがもう必要ないとわかったら、そのファイルをごみ箱から完全に削除できます。これも同様の理由です。
mp が提供するトゥームストーンは実装が非常に簡単です
application.yml
論理的な削除を構成するだけで済みます。
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为0)
# 若逻辑已删除和未删除的值和默认值一样,则可以不配置这2项
テストコード
package com.example.mp;
import com.example.mp.mappers.User2Mapper;
import com.example.mp.po.User2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class LogicDeleteTest {
@Autowired
private User2Mapper mapper;
@Test
public void testLogicDel() {
int i = mapper.deleteById(6);
System.out.println("rowAffected = " + i);
}
}
結果
ご覧のとおり、発行された SQL はもうありませんDELETE
が、UPDATE
この時点で再度実行しますSELECT
@Test
public void testSelect() {
List<User2> users = mapper.selectList(null);
}
発行された SQL ステートメントは、WHERE の後のロジックの削除解除条件を自動的に結合することがわかります。クエリ結果には、ID 6 の Wang Ruandan はありません。
論理削除の列を除いたSELECTの列が必要な場合は、次のようにエンティティクラスで@TableField
設定できます。
@TableField(select = false)
private Integer deleted;
以下の図の実行結果では、削除された列が SELECT に含まれなくなっていることがわかります。
前面でapplication.yml
行われる構成はグローバルです。一般的に、複数のテーブルの場合、論理的に削除されたフィールドの名前も統一し、論理的に削除された値と削除されていない値の値も統一するため、グローバル構成で十分です。もちろん、いくつかのテーブルを個別に設定したい場合は、エンティティ クラスの対応するフィールドでそれらを使用@TableLogic
できます。
@TableLogic(value = "0", delval = "1")
private Integer deleted;
まとめ
mp のトゥームストーンが有効になると、SQL に次のような影響があります。
-
INSERT ステートメント: 効果なし
-
SELECT ステートメント: WHERE 条件を追加し、削除されたデータを除外します
-
UPDATE ステートメント: 削除されたデータへの更新を防ぐために WHERE 条件を追加します。
-
DELETE 文: UPDATE 文に変換される
上記の効果は、mp によって自動的に挿入された SQL に対してのみ有効であることに注意してください。自分で手動で追加したカスタムSQLの場合は有効になりません。例えば
public interface User2Mapper extends BaseMapper<User2> {
@Select("select * from user2")
List<User2> selectRaw();
}
これを呼び出すとselectRaw
、mp のトゥームストーンは有効になりません。インタビューブック:https://www.yoodb.com
さらに、トゥームストーンはエンティティ クラスapplication.yml
内でグローバルに構成することも、@TableLogic
エンティティ クラス内でローカルに構成することもできます。
自動入力
多くの場合、テーブルには「新しい時刻」、「変更された時刻」、「演算子」などのフィールドがあります。より原始的な方法は、挿入または更新されるたびに手動で設定することです。mp は、いくつかのフィールドを自動的に入力するように設定できます。食用の例は次のとおりです。
1. エンティティクラスの一部のフィールドで@TableField
自動入力を設定する
public class User2 {
private Long id;
private String name;
private Integer age;
private String email;
private Long managerId;
@TableField(fill = FieldFill.INSERT) // 插入时自动填充
private LocalDateTime createTime;
@TableField(fill = FieldFill.UPDATE) // 更新时自动填充
private LocalDateTime updateTime;
private Integer version;
private Integer deleted;
}
2. 自動入力ハンドラーを実装する
package com.example.mp.component;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component //需要注册到Spring容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 插入时自动填充
// 注意第二个参数要填写实体类中的字段名称,而不是表的列名称
strictFillStrategy(metaObject, "createTime", LocalDateTime::now);
}
@Override
public void updateFill(MetaObject metaObject) {
// 更新时自动填充
strictFillStrategy(metaObject, "updateTime", LocalDateTime::now);
}
}
テスト
@Test
public void test() {
User2 user = new User2();
user.setId(8L);
user.setName("王一蛋");
user.setAge(29);
user.setEmail("[email protected]");
user.setManagerId(2L);
mapper.insert(user);
}
以下の図の結果によると、createTime が自動的に入力されることがわかります。
自動入力はフィールドが空の場合にのみ有効になることに注意してください。フィールドが空でない場合は、既存の値が直接使用されます。次のように
@Test
public void test() {
User2 user = new User2();
user.setId(8L);
user.setName("王一蛋");
user.setAge(29);
user.setEmail("[email protected]");
user.setManagerId(2L);
user.setCreateTime(LocalDateTime.of(2000,1,1,8,0,0));
mapper.insert(user);
}
更新時の自動入力、次のようにテストします
@Test
public void test() {
User2 user = new User2();
user.setId(8L);
user.setName("王一蛋");
user.setAge(99);
mapper.updateById(user);
}
オプティミスティックロックプラグイン
同時操作が発生した場合、各ユーザーのデータ操作が競合しないようにする必要があり、このとき同時実行性を制御する方法が必要です。悲観的ロックは、データベース内のレコードを変更する際に直接ロック(データベースのロック機構)を行い、データをロックしてから操作する方式であるのに対し、楽観的ロックはその名の通り競合が無いことを前提とし、実際にデータ操作を行う際には競合がないか確認してください。オプティミスティック ロックの一般的な実装はバージョン番号です。MySQL には MVCC と呼ばれるバージョン番号ベースの同時トランザクション制御もあります。
オプティミスティック ロックは、より多くの読み取りとより少ない書き込みが実行されるシナリオに適しており、ロック操作によって発生するパフォーマンスのオーバーヘッドを削減し、システムのスループットを向上させることができます。
書き込みが多くなり、読み取りが少なくなるシナリオでは、悲観的ロックの使用が多くなります。そうでない場合は、楽観的ロックの失敗や再試行により、パフォーマンスの低下につながります。
楽観的ロックは次のように実装されます。
-
レコードを取得するときに、現在のバージョンを取得します
-
更新する場合は、このバージョンを使用してください
-
更新を実行するときは、バージョン = newVersion (バージョン = oldVersion) を設定します。
-
oldVersion がデータベース内のバージョンと一致しない場合、更新は失敗します。
この考え方は CAS (Compare And Swap) に非常に似ています。
楽観的ロックの実装手順は次のとおりです。
1. オプティミスティックロックプラグインを設定する
package com.example.mp.config;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
/** 3.4.0以后的mp版本,推荐用如下的配置方式 **/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
/** 旧版mp可以采用如下方式。注意新旧版本中,新版的类,名称带有Inner, 旧版的不带, 不要配错了 **/
/*
@Bean
public OptimisticLockerInterceptor opLocker() {
return new OptimisticLockerInterceptor();
}
*/
}
2. エンティティ クラスのバージョンを表すフィールドに注釈を追加します。@Version
@Data
public class User2 {
private Long id;
private String name;
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
private LocalDateTime updateTime;
@Version
private Integer version;
private Integer deleted;
}
テストコード
@Test
public void testOpLocker() {
int version = 1; // 假设这个version是先前查询时获得的
User2 user = new User2();
user.setId(8L);
user.setEmail("[email protected]");
user.setVersion(version);
int i = mapper.updateById(user);
}
実行前にデータベースを確認する
下図の実行結果を見ると、SQL文にバージョンに関する操作が追加されていることがわかります。
UPDATE が 1 を返した場合、影響を受ける行の数が 1 であり、更新が成功したことを意味します。逆に、WHERE の背後にあるバージョンはデータベースと一致しないため、一致するレコードはなく、影響を受ける行の数は 0 となり、更新が失敗したことを示します。更新が成功すると、新しいバージョンがエンティティ オブジェクトにカプセル化されて戻されます。
エンティティ クラスのバージョン フィールド。タイプは int、long、Date、Timestamp、LocalDateTime のみをサポートします。
オプティミスティック ロック プラグインは**updateById(id)
とupdate(entity, wrapper)
メソッド**のみをサポートしていることに注意してください。
注: 使用した場合は再利用できませんwrapper
。wrapper
例は次のとおりです
@Test
public void testOpLocker() {
User2 user = new User2();
user.setId(8L);
user.setVersion(1);
user.setAge(2);
// 第一次使用
LambdaQueryWrapper<User2> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User2::getName, "王一蛋");
mapper.update(user, wrapper);
// 第二次复用
user.setAge(3);
mapper.update(user, wrapper);
}
wrapper
2 番目の再利用では、結合された SQL で、後続の WHERE ステートメントに 2 つのバージョンがあり、問題があることがわかります。
パフォーマンス分析プラグイン
このプラグインは、SQL ステートメントのパフォーマンス分析およびチューニングのために SQL ステートメントの実行時間を出力します。
注: バージョン 3.2.0 以降、mp に付属するパフォーマンス分析プラグインは正式に削除されたため、サードパーティのパフォーマンス分析プラグインを使用することをお勧めします。
食べるステップ
1. Maven 依存関係を導入する
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
2. 変更application.yml
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver #换成p6spy的驱动
url: jdbc:p6spy:mysql://localhost:3306/yogurt?serverTimezone=Asia/Shanghai #url修改
username: root
password: root
3.src/main/resources
リソースディレクトリに追加しますspy.properties
#spy.properties
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 真实JDBC driver , 多个以逗号分割,默认为空。由于上面设置了modulelist, 这里可以不用设置driverlist
#driverlist=com.mysql.cj.jdbc.Driver
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
#若要日志输出到文件, 把上面的appnder注释掉, 或者采用下面的appender, 再添加logfile配置
#不配置appender时, 默认是往文件进行输出的
#appender=com.p6spy.engine.spy.appender.FileLogger
#logfile=log.log
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
# 执行时间设置, 只有超过这个执行时间的才进行记录, 默认值0, 单位毫秒
executionThreshold=10
テストケースを実行するだけで、SQLの実行時間が記録されていることがわかります。
マルチテナントSQLパーサー
マルチテナントの概念: 複数のユーザーがシステムを共有しますが、データは比較的独立しており、ある程度の分離を維持する必要があります。
マルチテナントのデータ分離には通常、次の方法があります。
-
異なるテナントは異なるデータベース サーバーを使用します
利点は、異なるテナントに異なる独立したデータベースがあるため、拡張に役立ち、異なるテナントに合わせてより適切にパーソナライズできること、および障害が発生した場合のデータの復元が容易であることです。
欠点は、データベースの数が増加し、購入コストが増加し、保守コストが増加することです。
-
異なるテナントが同じデータベース サーバーを使用しますが、異なるデータベース (異なるスキーマ) を使用します。
メリットは購入費や維持費が安いことですが、デメリットは異なるテナントのデータがまとめられるため、データ復旧が難しくなることです。
-
異なるテナントが同じデータベース サーバーを使用し、同じデータベースを使用し、データ テーブルを共有し、区別するためにテーブルにテナント ID を追加します。
利点は、取得コストと保守コストが最も低く、サポートされるユーザー数が最も少ないことですが、欠点は、分離性が最も低く、セキュリティが最も低いことです。Java テクノロジーの高度なルート: https://www.yoodb.com
食用例は以下の通り
マルチテナントインターセプター構成を追加します。設定を追加した後、CRUDを実行すると、SQL文の最後にテナントID条件が自動的に結合されます。
package com.example.mp.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
// 返回租户id的值, 这里固定写死为1
// 一般是从当前上下文中取出一个 租户id
return new LongValue(1);
}
/**
** 通常会将表示租户id的列名,需要排除租户id的表等信息,封装到一个配置类中(如TenantConfig)
**/
@Override
public String getTenantIdColumn() {
// 返回表中的表示租户id的列名
return "manager_id";
}
@Override
public boolean ignoreTable(String tableName) {
// 表名不为 user2 的表, 不拼接多租户条件
return !"user2".equals(tableName);
}
}));
// 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor
// 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false
return interceptor;
}
}
テストコード
@Test
public void testTenant() {
LambdaQueryWrapper<User2> wrapper = new LambdaQueryWrapper<>();
wrapper.likeRight(User2::getName, "王")
.select(User2::getName, User2::getAge, User2::getEmail, User2::getManagerId);
user2Mapper.selectList(wrapper);
}
動的テーブル名 SQL パーサー
データ量が特に多い場合は、サブデータベースやサブテーブルを使用することが多いです。このとき、テーブル構造は同じでテーブル名が異なるテーブルが複数存在する場合があります。たとえばorder_1
、クエリを実行する場合、クエリ対象のテーブルの名前を動的に設定する必要がある場合がありますorder_2
。order_3
mp は動的テーブル名 SQL パーサーを提供します。実行可能な例は次のとおりです。
まずmysqlにuser2
テーブルをコピーします
動的テーブル名インターセプターの構成
package com.example.mp.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Random;
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
HashMap<String, TableNameHandler> map = new HashMap<>();
// 对于user2表,进行动态表名设置
map.put("user2", (sql, tableName) -> {
String _ = "_";
int random = new Random().nextInt(2) + 1;
return tableName + _ + random; // 若返回null, 则不会进行动态表名替换, 还是会使用user2
});
dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map);
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
return interceptor;
}
}
テスト
@Test
public void testDynamicTable() {
user2Mapper.selectList(null);
}
要約する
-
条件コンストラクターは
AbstractWrapper
、SQL ステートメントで WHERE 条件を構築するための複数のメソッドを提供し、そのサブクラスは、特定の列のみを選択するためのQueryWrapper
追加メソッドを提供しselect
、サブクラスは、 SQL で SET ステートメントを設定するためのUpdateWrapper
追加set
メソッドを提供します。通常のものに加えて、WHERE 条件を構築するときにメソッド参照を直接使用してWHERE 条件内の列を指定するなど、ラムダWrapper
式ベースのものもあります。これは文字列を介して指定するよりもエレガントです。さらに、チェーンされた Wrapperもあり、これをカプセル化してより便利に結果を取得できます。Wrapper
LambdaQueryWrapper
LambdaUpdateWrapper
Wrapper
LambdaQueryChainWrapper
BaseMapper
-
条件コンストラクターはチェーン呼び出しを使用して複数の条件を結合し、条件はデフォルトで
AND
接続されます。 -
AND
またはの後の条件OR
を括弧で囲む必要がある場合は、括弧内の条件をラムダ式の形式でパラメータとして渡すand()
か、or()
特に、()
WHERE ステートメントの先頭に配置する必要がある場合は、nested()
このメソッドを使用できます。
-
カスタム SQL ステートメントで条件式を渡す必要がある場合、またはデータベース関数を呼び出す必要がある場合、
apply()
SQL スプライシングにメソッドを使用できます。 -
条件コンストラクターの各メソッドは、
boolean
変数のタイプを使用してcondition
、必要に応じて WHERE 条件を柔軟に結合できます (condition
SQLtrue
ステートメントが結合される場合のみ)。 -
ラムダ条件コンストラクターを使用すると、ラムダ式による条件構築にエンティティ クラスの属性を直接使用できます。これは、通常の条件コンストラクターよりも洗練されています。また、公式アカウント Java 選考、Java 面接への回答、面接資料の入手も可能です。
-
mp が提供するメソッドが十分でない場合は、カスタム SQL (ネイティブ mybatis)の形式で拡張および開発できます。
-
ページング クエリに mp を使用する場合、ページング インターセプタ (Interceptor) を作成し、Spring コンテナに登録してから、ページング オブジェクト (Page オブジェクト) を渡してクエリを実行する必要があります。単一のテーブルをクエリする場合は、
BaseMapper
提供されたselectPage
orselectMapsPage
メソッドを使用できます。複雑なシナリオ (複数テーブルの結合クエリなど) では、カスタム SQL を使用します。 -
ARモードではエンティティクラスを操作することでデータベースを直接操作できます。エンティティクラスを継承させ
Model
ます