首页 > 其他分享 >MyBatis-Flex:超越Mybatis-plus的优雅魅力

MyBatis-Flex:超越Mybatis-plus的优雅魅力

时间:2023-08-23 11:01:13浏览次数:42  
标签:Flex account private 查询 plus Mybatis tb id 脱敏

MyBatis-Flex一个优雅的 MyBatis 增强框架。

更轻量

MyBatis-Flex 除了 MyBatis 本身,再无任何第三方依赖,因此会带来更高的自主性、把控性和稳定性。在任何一个系统中,依赖越多,稳定性越差。

更灵活

MyBatis-Flex 提供了非常灵活的 QueryWrapper,支持关联查询、多表查询、多主键、逻辑删除、乐观锁更新、数据填充、数据脱敏、等等....

更高的性能

MyBatis-Flex 通过独特的架构,没有任何 MyBatis 拦截器、在 SQL 执行的过程中,没有任何的 SQL Parse,因此会带来指数级的性能增长。

官网提供的和同类框架的功能对比

功能或特点

MyBatis-Flex

MyBatis-Plus

Fluent-MyBatis

对 entity 的基本增删改查

分页查询

分页查询之总量缓存

分页查询无 SQL 解析设计(更轻量,及更高性能)

多表查询: from 多张表

多表查询: left join、inner join 等等

多表查询: union,union all

单主键配置

多种 id 生成策略

支持多主键、复合主键

字段的 typeHandler 配置

除了 MyBatis,无其他第三方依赖(更轻量)

QueryWrapper 是否支持在微服务项目下进行 RPC 传输

未知

逻辑删除

乐观锁

SQL 审计

数据填充

✔️  (收费)

数据脱敏

✔️  (收费)

字段权限

✔️  (收费)

字段加密

✔️  (收费)

字典回写

✔️  (收费)

Db + Row

Entity 监听

多数据源支持

借助其他框架或收费

多数据源是否支持 Spring 的事务管理,比如 @Transactional 和 TransactionTemplate 等

多数据源是否支持 "非Spring" 项目

多租户

动态表名

动态 Schema

官网提供的和同类框架的性能对比

  • MyBatis-Flex 的查询单条数据的速度,大概是 MyBatis-Plus 的 5 ~ 10+ 倍。
  • MyBatis-Flex 的查询 10 条数据的速度,大概是 MyBatis-Plus 的 5~10 倍左右。
  • Mybatis-Flex 的分页查询速度,大概是 Mybatis-Plus 的 5~10 倍左右。
  • Mybatis-Flex 的数据更新速度,大概是 Mybatis-Plus 的 5~10+ 倍。

亮点功能

除了Mybatis-plus带的那些功能,Mybatis-Flex提供了多主键、复合主键功能;提供了关联查询;特别是关联查询在日常业务开发碰到的场景很多。

Mybatis-Flex提供了一对一、一对多、多对一、多对多的场景。

一对一关联查询 @RelationOneToOne

假设有一个账户,账户有身份证,账户和身份证的关系是一对一的关系,代码如下所示:

Account.java :

public class Account implements Serializable {

    @Id(keyType = KeyType.Auto)
    private Long id;

    private String userName;

    @RelationOneToOne(selfField = "id", targetField = "accountId")
    private IDCard idCard;

    //getter setter
}

IDCard.java :

@Table(value = "tb_idcard")
public class IDCard implements Serializable {

    private Long accountId;
    private String cardNo;
    private String content;

    //getter setter
}

@RelationOneToOne 配置描述:

  • selfField 当前实体类的属性
  • targetField 目标对象的关系实体类的属性

PS: 若 selfField 是主键,且当前表只有 1 个主键时,可以不填写。因此,以上的配置可以简化为 @RelationOneToOne(targetField = "accountId")

假设数据库 5 条 Account 数据,然后进行查询:

List<Account> accounts = accountMapper.selectAllWithRelations();
System.out.println(accounts);

其执行的 SQL 如下:

SELECT `id`, `user_name`, `age` FROM `tb_account`

SELECT `account_id`, `card_no`, `content` FROM `tb_idcard`
WHERE account_id IN (1, 2, 3, 4, 5)

查询打印的结果如下:

[
 Account{id=1, userName='孙悟空', age=18, idCard=IDCard{accountId=1, cardNo='0001', content='内容1'}},
 Account{id=2, userName='猪八戒', age=19, idCard=IDCard{accountId=2, cardNo='0002', content='内容2'}},
 Account{id=3, userName='沙和尚', age=19, idCard=IDCard{accountId=3, cardNo='0003', content='内容3'}},
 Account{id=4, userName='六耳猕猴', age=19, idCard=IDCard{accountId=4, cardNo='0004', content='内容4'}},
 Account{id=5, userName='王麻子叔叔', age=19, idCard=IDCard{accountId=5, cardNo='0005', content='内容5'}}
 ]

一对多关联查询 @RelationOneToMany

假设一个账户有很多本书籍,一本书只能归属一个账户所有;账户和书籍的关系是一对多的关系,代码如下:

Account.java :

public class Account implements Serializable {

    @Id(keyType = KeyType.Auto)
    private Long id;

    private String userName;

    @RelationOneToMany(selfField = "id", targetField = "accountId")
    private List<Book> books;

    //getter setter
}

Book.java :

@Table(value = "tb_book")
public class Book implements Serializable {

    @Id(keyType = KeyType.Auto)
    private Long id;
    private Long accountId;
    private String title;

    //getter setter
}

@RelationOneToMany 配置描述:

  • selfField 当前实体类的属性
  • targetField 目标对象的关系实体类的属性

PS: 若 selfField 是主键,且当前表只有 1 个主键时,可以不填写。因此,以上的配置可以简化为 @RelationOneToOne(targetField = "accountId")

假设数据库 5 条 Account 数据,然后进行查询:

List<Account> accounts = accountMapper.selectAllWithRelations();
System.out.println(accounts);

其执行的 SQL 如下:

SELECT `id`, `user_name`, `age` FROM `tb_account`

SELECT `id`, `account_id`, `title`, `content` FROM `tb_book`
WHERE account_id IN (1, 2, 3, 4, 5)

Map 映射

若 Account.books 是一个 Map,而非 List,那么,我们需要通过配置 mapKeyField 来指定使用用个列来充当 Map 的 Key, 如下代码所示:

java

public class Account implements Serializable {

    @Id(keyType = KeyType.Auto)
    private Long id;

    private String userName;

    @RelationOneToMany(selfField = "id", targetField = "accountId"
        , mapKeyField = "id") //使用 Book 的 id 来填充这个 map 的 key
    private Map<Long, Book> books;

    //getter setter
}

多对多注解 @RelationManyToMany 也是如此。

多对一关联查询 @RelationManyToOne

假设一个账户有很多本书籍,一本书只能归属一个账户所有;账户和书籍的关系是一对多的关系,书籍和账户的关系为多对一的关系,代码如下:

Account.java:

public class Account implements Serializable {

    @Id(keyType = KeyType.Auto)
    private Long id;

    private String userName;

    //getter setter
}

Book.java 多对一的配置:

@Table(value = "tb_book")
public class Book implements Serializable {

    @Id(keyType = KeyType.Auto)
    private Long id;
    private Long accountId;
    private String title;

    @RelationManyToOne(selfField = "accountId", targetField = "id")
    private Account account;

    //getter setter
}

@RelationManyToOne 配置描述:

  • selfField 当前实体类的属性
  • targetField 目标对象的关系实体类的属性

PS: 若 targetField 目标对象的是主键,且目标对象的表只有 1 个主键时,可以不填写。因此,以上的配置可以简化为 @RelationManyToOne(selfField = "accountId")

多对多关联查询 @RelationManyToMany

假设一个账户可以有多个角色,一个角色也可以有多个账户,他们是多对多的关系,需要通过中间件表 tb_role_mapping 来维护:

tb_role_mapping 的表结构如下:

CREATE TABLE  `tb_role_mapping`
(
    `account_id`  INTEGER ,
    `role_id`  INTEGER
);

Account.java 多对多的配置:

public class Account implements Serializable {

    @Id(keyType = KeyType.Auto)
    private Long id;
    private String userName;

    @RelationManyToMany(
            joinTable = "tb_role_mapping", // 中间表
            selfField = "id", joinSelfColumn = "account_id",
            targetField = "id", joinTargetColumn = "role_id"
    )
    private List<Role> roles;

    //getter setter
}

Role.java 多对多的配置:

@Table(value = "tb_role")
public class Role implements Serializable {

    private Long id;
    private String name;

    //getter setter
}

@RelationManyToMany 配置描述:

  • selfField 当前实体类的属性
  • targetField 目标对象的关系实体类的属性
  • joinTable 中间表
  • joinSelfColumn 当前表和中间表的关系字段
  • joinTargetColumn 目标表和中间表的关系字段

注意:selfField 和 targetField 配置的是类的属性名,joinSelfColumn 和 joinTargetColumn 配置的是中间表的字段名。

若 selfField 和 targetField 分别是两张关系表的主键,且表只有 1 个主键时,可以不填写。因此,以上配置可以简化如下:

public class Account implements Serializable {

    @Id(keyType = KeyType.Auto)
    private Long id;
    private String userName;

    @RelationManyToMany(
            joinTable = "tb_role_mapping", // 中间表
            joinSelfColumn = "account_id",
            joinTargetColumn = "role_id"
    )
    private List<Role> roles;

    //getter setter
}

父子关系查询

比如在一些系统中,比如菜单会有一些父子关系,例如菜单表如下:

CREATE TABLE `tb_menu`
(
    `id`        INTEGER auto_increment,
    `parent_id`        INTEGER,
    `name`      VARCHAR(100)
);

Menu.java 定义如下:

@Table(value = "tb_menu")
public class Menu implements Serializable {

    private Long id;

    private Long parentId;

    private String name;

    @RelationManyToOne(selfField = "parentId", targetField = "id")
    private Menu parent;

    @RelationOneToMany(selfField = "id", targetField = "parentId")
    private List<Menu> children;

    //getter setter
}

查询顶级菜单:

QueryWrapper qw = QueryWrapper.create();
qw.where(MENU.PARENT_ID.eq(0));

List<Menu> menus = menuMapper.selectListWithRelationsByQuery(qw);
System.out.println(JSON.toJSONString(menus));

SQL 执行如下:

SELECT `id`, `parent_id`, `name` FROM `tb_menu` WHERE `parent_id` = 0
SELECT `id`, `parent_id`, `name` FROM `tb_menu` WHERE id = 0
SELECT `id`, `parent_id`, `name` FROM `tb_menu` WHERE parent_id IN (1, 2, 3)

JSON 输出内容如下:

[  {    "children": [      {        "id": 4,        "name": "子菜单",        "parentId": 1      },      {        "id": 5,        "name": "子菜单",        "parentId": 1      }    ],
    "id": 1,
    "name": "顶级菜单1",
    "parentId": 0
  },
  {
    "children": [],
    "id": 2,
    "name": "顶级菜单2",
    "parentId": 0
  },
  {
    "children": [
      {
        "id": 6,
        "name": "子菜单",
        "parentId": 3
      },
      {
        "id": 7,
        "name": "子菜单",
        "parentId": 3
      },
      {
        "id": 8,
        "name": "子菜单",
        "parentId": 3
      }
    ],
    "id": 3,
    "name": "顶级菜单3",
    "parentId": 0
  }
]

在以上的父子关系查询中,默认的递归查询深度为 3 个层级,若需要查询指定递归深度,需要添加如下配置:

QueryWrapper qw = QueryWrapper.create();
qw.where(MENU.PARENT_ID.eq(0));

//设置递归查询深度为 10 层
RelationManager.setMaxDepth(10);
List<Menu> menus = menuMapper.selectListWithRelationsByQuery(qw);

RelationManager.setMaxDepth(10) 的配置,只在当前第一次查询有效,查询后会清除设置。

MyBatis-Flex 逻辑删除

假设在 tb_account 表中,存在一个为 is_deleted 的字段,用来标识该数据的逻辑删除,那么 tb_account 表 对应的 "Account.java" 实体类应该配置如下:

@Table("tb_account")
public class Account {

    @Column(isLogicDelete = true)
    private Boolean isDelete;
    
    //Getter Setter...
}

此时,当我们执行如下的删除代码是:

accountMapper.deleteById(1);

MyBatis 执行的 SQL 如下:

UPDATE `tb_account` SET `is_delete` = 1 
WHERE `id` = ? AND `is_delete` = 0

可以看出,当执行 deleteById 时,MyBatis 只是进行了 update 操作,而非 delete 操作。

注意事项

当 "tb_account" 的数据被删除时( is_delete = 1 时),我们通过 MyBatis-Flex 的 selectOneById 去查找数据时,会查询不到数据。 原因是 selectOneById 会自动添加上 is_delete = 0 条件,执行的 sql 如下:

SELECT * FROM tb_account where id = ? and is_delete = 0

不仅仅是 selectOneById 方法会添加 is_delete = 0 条件,BaseMapper 的以下方法也都会添加该条件:

  • selectOneBy**
  • selectListBy**
  • selectCountBy**
  • paginate

同时,比如 Left Join 或者子查询等,若 子表也设置了逻辑删除字段, 那么子表也会添加相应的逻辑删除条件,例如:

QueryWrapper query1 = QueryWrapper.create()
    .select()
    .from(ACCOUNT)
    .leftJoin(ARTICLE).as("a").on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
    .where(ACCOUNT.AGE.ge(10));

其执行的 SQL 如下:

SELECT * FROM `tb_account` 
    LEFT JOIN `tb_article` AS `a` ON `tb_account`.`id` = `a`.`account_id` 
WHERE `tb_account`.`age` >= 10 
  AND `tb_account`.`is_delete` = 0 AND `a`.`is_delete` = 0

自动添加上 tb_account.is_delete = 0 AND a.is_delete = 0 条件。

示例 2:

QueryWrapper query2 = QueryWrapper.create()
    .select()
    .from(ACCOUNT)
    .leftJoin(
            //子查询
            select().from(ARTICLE).where(ARTICLE.ID.ge(100))
    ).as("a").on(
            ACCOUNT.ID.eq(raw("a.id"))
    )
    .where(ACCOUNT.AGE.ge(10));

其执行的 SQL 如下:

SELECT * FROM `tb_account` 
    LEFT JOIN (
        SELECT * FROM `tb_article` WHERE `id` >= 100 AND `is_delete` = 0
        ) AS `a` 
    ON `tb_account`.`id` = a.id 
WHERE `tb_account`.`age` >= 10 AND `tb_account`.`is_delete` = 0

数据脱敏

数据脱敏是什么

随着《网络安全法》的颁布施行,对个人隐私数据的保护已经上升到法律层面。 数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形, 实现敏感隐私数据的可靠保护。在涉及客户安全数据或者一些商业性敏感数据的情况下,在不违反系统规则条件下,对真实数据进行改造并提供使用, 如身份证号、手机号、卡号、客户号等个人信息都需要进行数据脱敏。

@ColumnMask

MyBatis-Flex 提供了 @ColumnMask() 注解,以及内置的 9 种脱敏规则,帮助开发者方便的进行数据脱敏。例如:

java

@Table("tb_account")
public class Account {

    @Id(keyType = KeyType.Auto)
    private Long id;

    @ColumnMask(Masks.CHINESE_NAME)
    private String userName;
}

以上的示例中,使用了 CHINESE_NAME 的脱敏规则,其主要用于处理 "中文名字" 的场景。当我们查询到 userName 为 张三丰 的时候,其内容自动被处理成 张**

除此之外,MyBatis-Flex 还提供了如下的 8 中脱敏规则(共9种),方便开发者直接使用:

  • 手机号脱敏
  • 固定电话脱敏
  • 身份证号脱敏
  • 车牌号脱敏
  • 地址脱敏
  • 邮件脱敏
  • 密码脱敏
  • 银行卡号脱敏

自定义脱敏规则

当 Mybaits-Flex 内置的 9 中脱敏规则无法满足要求时,我们还可以自定义脱敏规则,其步骤如下:

1、通过 MaskManager 注册新的脱敏规则:

MaskManager.registerMaskProcesser("自定义规则名称"
        , data -> {
            return data;
        })

2、使用自定义的脱敏规则

@Table("tb_account")
public class Account {

    @Id(keyType = KeyType.Auto)
    private Long id;

    @ColumnMask("自定义规则名称")
    private String userName;
}

取消脱敏处理

在某些场景下,程序希望查询得到的数据是原始数据,而非脱敏数据。比如要去查询用户的手机号,然后给用户发送短信。又或者说,我们进入编辑页面编辑用户数据, 如果编辑页面展示的是脱敏数据,然后再次点击保存,那么数据库的真实数据也会被脱敏覆盖。

因此,MaskManager 提供了 execWithoutMaskskipMaskrestoreMask 三个方法来处理这种场景:

推荐使用execWithoutMask方法,该方法使用了模版方法设计模式,保障跳过脱敏处理并执行相关逻辑后自动恢复脱敏处理。

execWithoutMask方法实现如下:

public static <T> T execWithoutMask(Supplier<T> supplier) {
    try {
        skipMask();
        return supplier.get();
    } finally {
        restoreMask();
    }
}

使用方法:

AccountMapper mapper = ...;
List<Account> accounts = MaskManager.execWithoutMask(mapper::selectAll);
System.out.println(accounts);

skipMaskrestoreMask方法需配套使用,推荐使用try{...}finally{...}模式,如下例所示。 使用这两个方法可以自主控制跳过脱敏处理和恢复脱敏处理的时机。 当跳过脱敏处理和恢复脱敏处理无法放在同一个方法中时,可以使用这两个方法。 此时需要仔细处理代码分支及异常,以防止跳过脱敏处理后未恢复脱敏处理,导致安全隐患。

try {
    MaskManager.skipMask();
    
    //此处查询到的数据不会进行脱敏处理
    accountMapper.selectListByQuery(...);
} finally {
    MaskManager.restoreMask();
}

SQL 审计

SQL 审计是一项非常重要的工作,是企业数据安全体系的重要组成部分,通过 SQL 审计功能为数据库请求进行全程记录,为事后追溯溯源提供了一手的信息,同时可以通过可以对恶意访问及时警告管理员,为防护策略优化提供数据支撑。

同时、提供 SQL 访问日志长期存储,满足等保合规要求。

开启审计功能^1.0.5

Mybaits-Flex 的 SQL 审计功能,默认是关闭的,若开启审计功能,许添加如下配置。

AuditManager.setAuditEnable(true)

默认情况下,Mybaits-Flex 的审计消息(日志)只会输出到控制台,如下所示:

>>>>>>Sql Audit: {platform='mybatis-flex', module='null', url='null', user='null', userIp='null', hostIp='192.168.3.24', query='SELECT * FROM `tb_account` WHERE `id` = ?', queryParams=[1], queryTime=1679991024523, elapsedTime=1}
>>>>>>Sql Audit: {platform='mybatis-flex', module='null', url='null', user='null', userIp='null', hostIp='192.168.3.24', query='SELECT * FROM `tb_account` WHERE `id` = ?', queryParams=[1], queryTime=1679991024854, elapsedTime=3}
>>>>>>Sql Audit: {platform='mybatis-flex', module='null', url='null', user='null', userIp='null', hostIp='192.168.3.24', query='SELECT * FROM `tb_account` WHERE `id` = ?', queryParams=[1], queryTime=1679991025100, elapsedTime=2}

Mybaits-Flex 消息包含了如下内容:

  • platform:平台,或者是运行的应用
  • module:应用模块
  • url:执行这个 SQL 涉及的 URL 地址
  • user:执行这个 SQL 涉及的 平台用户
  • userIp:执行这个 SQL 的平台用户 IP 地址
  • hostIp:执行这个 SQL 的服务器 IP 地址
  • query:SQL 内容
  • queryParams:SQL 参数
  • queryTime:SQL 执行的时间点(当前时间)
  • elapsedTime:SQL 执行的消耗时间(毫秒)
  • metas:其他扩展元信息

事务管理

MyBatis-Flex 提供了一个名为 Db.tx() 的方法^1.0.6,用于进行事务管理,若使用 Spring 框架的场景下,也可使用 @Transactional 注解进行事务管理。

Db.tx() 方法定义如下:

boolean tx(Supplier<Boolean> supplier);
boolean tx(Supplier<Boolean> supplier, Propagation propagation);

<T> T txWithResult(Supplier<T> supplier);
<T> T txWithResult(Supplier<T> supplier, Propagation propagation);

方法:

  • tx:返回结果为 Boolean,返回 null 或者 false 或者 抛出异常,事务回滚
  • txWithResult:返回结果由 Supplier 参数决定,只有抛出异常时,事务回滚

参数:

  • supplier:要执行的内容(代码)
  • propagation:事务传播属性

事务传播属性 propagation 是一个枚举类,其枚举内容如下:

//若存在当前事务,则加入当前事务,若不存在当前事务,则创建新的事务
REQUIRED(0),

//若存在当前事务,则加入当前事务,若不存在当前事务,则已非事务的方式运行
SUPPORTS(1),

//若存在当前事务,则加入当前事务,若不存在当前事务,则抛出异常
MANDATORY(2),

//始终以新事务的方式运行,若存在当前事务,则暂停(挂起)当前事务。
REQUIRES_NEW(3),

//以非事务的方式运行,若存在当前事务,则暂停(挂起)当前事务。
NOT_SUPPORTED(4),

//以非事务的方式运行,若存在当前事务,则抛出异常。
NEVER(5),

//暂时不支持
NESTED(6),

Db.tx() 代码示例:

Db.tx(() -> {

    //进行事务操作

    return true;
});

若 tx() 方法抛出异常,或者返回 false,或者返回 null,则回滚事务。只有正常返回 true 的时候,进行事务提交。

嵌套事务

示例代码:

Db.tx(() -> {

    //进行事务操作

    boolean success = Db.tx(() -> {
        //另一个事务的操作
        return true;
    });


    return true;
});

支持无限极嵌套,默认情况下,嵌套事务直接的关系是:REQUIRED(若存在当前事务,则加入当前事务,若不存在当前事务,则创建新的事务)。

@Transactional

MyBatis-Flex 已支持 Spring 框架的 @Transactional,在使用 SpringBoot 的情况下,可以直接使用 @Transactional 进行事务管理。 同理,使用 Spring 的 TransactionTemplate 进行事务管理也是没问题的。

注意:若项目未使用 SpringBoot,只用到了 Spring,需要参考 MyBatis-Flex 的 FlexTransactionAutoConfiguration 进行事务配置,才能正常使用 @Transactional 注解。

特征

  • 1、支持嵌套事务
  • 2、支持多数据源

注意:在多数据源的情况下,所有数据源的数据库请求(Connection)会执行相同的 commit 或者 rollback,但并非原子操作。例如:

@Transactional
public void doSomething(){

    try{
        DataSourceKey.use("ds1");
        Db.updateBySql("update ....");
    }finally{
        DataSourceKey.clear()
    }

    try{
        DataSourceKey.use("ds2");
        Db.updateBySql("update ...");
    }finally{
        DataSourceKey.clear()
    }

    //抛出异常
    int x = 1/0;
}

在以上的例子中,两次 Db.update(...) 虽然是两个不同的数据源,但它们都在同一个事务 @Transactional 里,因此,当抛出异常的时候, 它们都会进行回滚(rollback)。

以上提到的 并非原子操作,指的是:

假设在回滚的时候,恰好其中一个数据库出现了异常(比如 网络问题,数据库崩溃),此时,可能只有一个数据库的数据正常回滚(rollback)。 但无论如何,MyBatis-Flex 都会保证在同一个 @Transactional 中的多个数据源,保持相同的 commit 或者 rollback 行为。

字段权限

字段权限,指的是在一张表中设计了许多字段,但是不同的用户(或者角色)查询,返回的字段结果是不一致的。 比如:tb_account 表中,有 user_name 和 password 字段,但是 password 字段只允许用户本人查询, 或者超级管理员查询,这种场景下,我们会用到 字段权限 的功能。

在 @Table() 注解中,有一个配置名为 onSet,用于设置这张表的 设置 监听,这里的 设置 监听指的是: 当我们使用 sql 、调用某个方法去查询数据,得到的数据内容映射到 entity 实体,mybatis 通过 setter 方法去设置 entity 的值时的监听。

以下是示例:

step 1:  为实体类编写一个 set 监听器(SetListener

public class AccountOnSetListener implements SetListener {
    @Override
    public Object onSet(Object entity, String property, Object value) {
        if (property.equals("password")){

            //去查询当前用户的权限
            boolean hasPasswordPermission = getPermission();
            
            //若没有权限,则把数据库查询到的 password 内容修改为 null
            if (!hasPasswordPermission){
                value = null;
            }
        }
        return value;
    }
}

step 2:  为实体类配置 onSet 监听

@Table(value = "tb_account", onSet = AccountOnSetListener.class)
public class Account {

    @Id(keyType = KeyType.Auto)
    private Long id;

    private String userName;
    
    private String password;
    
    //getter setter
}

字段加密

字段加密,指的是数据库在存入了明文内容,但是当我们进行查询时,返回的内容为加密内容,而非明文内容。

以下是 MyBatis-Flex 字段加密示例:

step 1:  为实体类编写一个 set 监听器(SetListener

public class AccountOnSetListener implements SetListener {
    @Override
    public Object onSet(Object entity, String property, Object value) {
        
        if (value != null){
            //对字段内容进行加密
            value = encrypt(value);
        }
        
        return value;
    }
}

step 2:  为实体类配置 onSet 监听

@Table(value = "tb_account", onSet = AccountOnSetListener.class)
public class Account {

    @Id(keyType = KeyType.Auto)
    private Long id;

    private String userName;
    
    private String password;
    
    //getter setter
}

标签:Flex,account,private,查询,plus,Mybatis,tb,id,脱敏
From: https://blog.51cto.com/u_16205813/7200334

相关文章

  • 02.前后端分离中台框架前端 admin.ui.plus 学习-介绍与简单使用
    中台框架前台项目admin.ui.plus的初识基于vue3.x+CompositionAPIsetup语法糖+typescript+vite+elementplus+vue-router-next+pinia技术,内置支持一键生成微服务接口,适配手机、平板、pc的后台权限管理框架,希望减少工作量,帮助大家实现快速开发。框架一览......
  • mybatis <foreach>标签动态增删改查方式
    摘要目录<foreach>标签动态增删改查mybatis<foreach>实战有了建表以及插入,当然少不了删除和更新mapper.xml中<foreach&目录<foreach>标签动态增删改查mybatis<foreach>实战有了建表以及插入,当然少不了删除和更新mapper.xml中<foreach>标签使用适用场景 <......
  • flex为1的时候,子元素限制一行时内容撑出父元素布局解决方案
    解决方法:1、在子元素的父级加overflow:hidden;2、在子元素的父级上加width:0;或者height:0;相关问题:当子元素是自适应父元素的情况下,但子元素大小超出了父元素,在低版本浏览器(我的是v85.0.4183.83)会出现宽度/高度自动无限增大的bug,将父元素无限撑大......
  • DataSecurity Plus:守护企业数据安全的坚实屏障
    在数字化时代,数据被誉为企业最重要的资产之一。然而,随着大数据的兴起和信息的日益增长,企业面临着前所未有的数据安全挑战。为了应对这些挑战,数据安全管理变得至关重要。在这个领域,ManageEngine的DataSecurityPlus是一款备受信赖的解决方案。DataSecurityPlus一、DataSecurityPlu......
  • mybatisplus中lambdaQuery()与lambdaUpdate()的使用
    这篇“mybatisplus中lambdaQuery()与lambdaUpdate()怎么使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“mybatisplus中lambdaQuery()与lambdaUpdate()怎么使用”......
  • SpringBoot复习:(44)MyBatisAutoConfiguration
    可以看到MyBatisAutoConfiguration引入了MyBatisProperties这个属性:MyBatisAutoConfiguration中配置了一个SqlSessionFactoryBean,代码如下:可以配置mybatis-config.xml,需要配置文件里指定:mybatis.config-locatinotallow=classpath:/mybatis-config.xml同样可配置MyBatis的xml......
  • 角度新奇!第一次看到这样使用MyBatis的,看得我一愣一愣的。
    你好呀,我是歪歪。这期给大家分享一个读者给我分享的一个关于MyBatis的“编程小技巧”,说真的,这骚操作,直接把我看得一愣一愣的。我更情愿叫它:坑你没商量之埋雷大法。Demo为了让你丝滑入戏,我还是先给你搞个Demo。因为要使用到MyBatis嘛,所以我们先搞两个表。一个表叫做p......
  • element-plus使用中文
    element-plus默认是英文,要显示中文需要安装中文包1.ElementPlus提供了全局配置国际化的配置。importElementPlusfrom'element-plus'importzhCnfrom'element-plus/es/locale/lang/zh-cn'app.use(ElementPlus,{locale:zhCn})2.ElementPlus还提供了一个Vue组......
  • flex布局
    flex布局原理flex是flexibleBox的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性,任何一个容器都可以指定为flex布局。当我们为父盒子设为flex布局以后,子元素的float、clear和vertical-align属性将失效。flex布局又叫伸缩布局、弹性布局、伸缩盒布局、......
  • java.lang.NoSuchMethodError: com.baomidou.mybatisplus.core.toolkit.StringUtils.i
    在运行springboot项目的时候爆java.lang.NoSuchMethodError:com.baomidou.mybatisplus.core.toolkit.StringUtils.isNotBlank(Ljava/lang/CharSequence;)Z错误错误如下11:49:08.390[restartedMain]DEBUGorg.springframework.boot.context.logging.ClasspathLoggingApplica......