项目应用实践:基于grpc从零开始搭建一个准生产分布式应用(0) - quickStart
这部分功能不是太常用,但一旦有合适的场景会省很多麻烦,同时也要和开发同学制定好规范,主要的使用有几下几类场景
一、自定义类
自动填充功能-扩展MetaObjectHandler
//这块一般会做成一个BaseEntity.java类
public class User {
(fill = FieldFill.INSERT_UPDATE)
private String operator;
}
public class MyMetaObjectHandler implements MetaObjectHandler {
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "operator", String.class, "Jetty");
}
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "operator", String.class, "Tom");
}
}
this.strictUpdateFill(metaObject, "updateDatetime1", LocalDateTime.class, LocalDateTime.now())
.strictUpdateFill(metaObject, "updateDatetime", LocalDateTime.class, LocalDateTime.now());
类型处理器-扩展TypeHandler
(chain = true)
(autoResultMap = true)
public class User {
private Long id;
private String name;
private Integer age;
private String email;
/**
* 注意!! 必须开启映射注解
*
* @TableName(autoResultMap = true)
* <p>
* 以下两种类型处理器,二选一 也可以同时存在
* <p>
* 注意!!选择对应的 JSON 处理器也必须存在对应依赖包
*/
(typeHandler = WalletListTypeHandler.class)
private List<Wallet> wallets;
(typeHandler = FastjsonTypeHandler.class) //这是mybatis默认的
private OtherInfo otherInfo;
}
/**
* 自定义复杂类型处理器<br/>
* 不要问我为什么要重写 parse 因为顶层父类是无法获取到准确的待转换复杂返回类型数据
*/
public class WalletListTypeHandler extends JacksonTypeHandler {
public WalletListTypeHandler(Class<?> type) {
super(type);
}
protected Object parse(String json) {
try {
return getObjectMapper().readValue(json, new TypeReference<List<Wallet>>() {
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
自定义ID生成器-扩展IdentifierGenerator
public class User {
(type = IdType.ASSIGN_ID)
private Long id;
private String name;
private Integer age;
private String email;
}
//下面这个类不需要配置,只要用spring注解后,自动起作用
public class CustomIdGenerator implements IdentifierGenerator {
private final AtomicLong al = new AtomicLong(1);
public Long nextId(Object entity) {
//可以将当前传入的class全类名来作为bizKey,或者提取参数来生成bizKey进行分布式Id调用生成.
String bizKey = entity.getClass().getName();
log.info("bizKey:{}", bizKey);
MetaObject metaObject = SystemMetaObject.forObject(entity);
String name = (String) metaObject.getValue("name");
final long id = al.getAndAdd(1);
log.info("为{}生成主键值->:{}", name, id);
return id;
}
}
id BIGINT(20) NOT NULL COMMENT '主键ID',
---------------------以上适用用ID,下面的例子适用于string方式----------------------------------------
/**
* 字符串自增 ID
*/
(type = IdType.AUTO)
private String id;
id VARCHAR(30) AUTO_INCREMENT COMMENT '主键ID',
---------------------这个例子稍复杂一点,它可为各别表指定主键生成方式------------------------------------------
("SEQ_USER")
("user")
public class User {
(value = "id", type = IdType.INPUT)
private Long id;
}
CREATE TABLE user
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
CREATE SEQUENCE IF NOT EXISTS SEQ_USER START WITH 10086 INCREMENT BY 1;
public class MpConfig {
public GlobalConfig globalConfiguration() {
GlobalConfig conf = new GlobalConfig();
conf.setDbConfig(new GlobalConfig.DbConfig().setKeyGenerators(Arrays.asList(
// h2 1.x 的写法(默认 2.x 的写法)
new IKeyGenerator() {
public String executeSql(String incrementerName) {
return "select " + incrementerName + ".nextval";
}
public DbType dbType() {
return DbType.POSTGRE_SQL;
}
}
)));
return conf;
}
}
二、插件
动态获取表名称-DynamicTableNameInnerInterceptor插件
("com.baomidou.mybatisplus.samples.dytablename.mapper")
public class MybatisPlusConfig {
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
dynamicTableNameInnerInterceptor.setTableNameHandler((sql, tableName) -> {
// 获取参数方法
Map<String, Object> paramMap = RequestDataHelper.getRequestData();
paramMap.forEach((k, v) -> System.err.println(k + "----" + v));
String year = "_2018";
int random = new Random().nextInt(10);
if (random % 2 == 1) {
year = "_2019";
}
return tableName + year;
});
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
// 3.4.3.2 作废该方式
// dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map);
return interceptor;
}
}
/**
* 请求参数传递辅助类
*/
public class RequestDataHelper {
/**
* 请求参数存取
*/
private static final ThreadLocal<Map<String, Object>> REQUEST_DATA = new ThreadLocal<>();
/**
* 设置请求参数
*
* @param requestData 请求参数 MAP 对象
*/
public static void setRequestData(Map<String, Object> requestData) {
REQUEST_DATA.set(requestData);
}
/**
* 获取请求参数
*
* @param param 请求参数
* @return 请求参数 MAP 对象
*/
public static <T> T getRequestData(String param) {
Map<String, Object> dataMap = getRequestData();
if (CollectionUtils.isNotEmpty(dataMap)) {
return (T) dataMap.get(param);
}
return null;
}
/**
* 获取请求参数
*
* @return 请求参数 MAP 对象
*/
public static Map<String, Object> getRequestData() {
return REQUEST_DATA.get();
}
}
void test() {
RequestDataHelper.setRequestData(new HashMap<String, Object>() {{
put("id", 123);
put("hello", "tomcat");
put("name", "汤姆凯特");
}});
// 自己去观察打印 SQL 目前随机访问 user_2018 user_2019 表
for (int i = 0; i < 6; i++) {
User user = userMapper.selectById(1);
System.err.println(user.getName());
}
}
防止全表更新或删除操作-BlockAttackInnerInterceptor插件
public class MybatisPlusConfig {
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
}
乐观锁-OptimisticLockerInnerInterceptor插件
public class MybatisPlusOptLockerConfig {
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
public class User {
(value = "id", type = IdType.AUTO)
private Long id;
private Integer version; //注意这个version注解
}
分页-PaginationInnerInterceptor插件
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
三、注解
逻辑删除-@TableLogic
这个完全是用注解实现的,注释是数据库DDL定义
private Integer deleted; //deleted INT(11) NOT NULL DEFAULT 0,
(delval = "null", value = "1")
private Integer deleted;//deleted INT(11),
(delval = "now()", value = "null")
private LocalDateTime delTime; //del_time TIMESTAMP,
实体映射-@ResultMap
"userChildrenMap")(
("<script>select u.id,u.name,u.email,u.age,c.id as \"c_id\",c.name as \"c_name\",c.user_id as \"c_user_id\" " +
"from user u " +
"left join children c on c.user_id = u.id " +
"<where>" +
"<if test=\"selectInt != null\"> " +
"and u.age = #{selectInt} " +
"</if> " +
"<if test=\"selectStr != null and selectStr != ''\"> " +
"and c.name = #{selectStr} " +
"</if> " +
"</where>" +
"</script>")
MyPage<UserChildren> userChildrenPage(MyPage<UserChildren> myPage);
<resultMap id="userChildrenMap" type="com.baomidou.mybatisplus.samples.pagination.model.UserChildren">
<id column="id" property="id"/>
<result column="age" property="age"/>
<result column="email" property="email"/>
<result column="name" property="name"/>
<collection property="c" ofType="com.baomidou.mybatisplus.samples.pagination.entity.Children" columnPrefix="c_">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="user_id" property="userId"/>
</collection>
</resultMap>
(resultMap = "m_b") // 对应xml里的 id
-----------------------------------------------
public class Man {}
<resultMap id="m_b" type="com.baomidou.mybatisplus.samples.resultmap.entity.Man">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="laoPoId" column="lao_po_id"/>
</resultMap>
-----------------------------------------------
<select id="selectLinkById" resultMap="m_r">
select *
from man
where id = #{id}
</select>