一、JPA 审计
实体保存、更新时要保留操作人以及操作时间。
- 实体基类
/**
* @author punk
* @date 2021/02/20
*/
@MappedSuperclass
@EntityListeners(PaasAuditingEntityListener.class)
public abstract class BaseEntity<T> implements Serializable {
private static final long serialVersionUID = 5434329574866971414L;
/**
* 创建者
*/
@CreatedBy
@Column(name = "create_by", updatable = false)
private String createBy;
/**
* 创建者名
*/
@Column(name = "create_by_name", updatable = false)
private String createByName;
/**
* 创建时间
*/
@CreatedDate
@Column(name = "create_time", updatable = false)
private Date createTime;
/**
* 修改人
*/
@LastModifiedBy
private String updateBy;
/**
* 修改人名
*/
private String updateByName;
/**
* 修改时间
*/
@LastModifiedDate
private Date updateTime;
/**
* 数据隐藏
*/
private Boolean disabled;
/**
* 获取主键
* @return
*/
public abstract T getId();
/**
* 主键赋值
* @param id
*/
public abstract void setId(T id);
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public String getCreateByName() {
return createByName;
}
public void setCreateByName(String createByName) {
this.createByName = createByName;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public String getUpdateByName() {
return updateByName;
}
public void setUpdateByName(String updateByName) {
this.updateByName = updateByName;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public Boolean getDisabled() {
return disabled;
}
public void setDisabled(Boolean disabled) {
this.disabled = disabled;
}
}
- 审计器接口
/**
* 平台审计处理类
* @author Punk
* @date 2020/05/19
*/
public interface AuditHandler {
/**
* 保存前
* @param obj
*/
public void prePersist(Object obj) ;
/**
* 更新前
* @param obj
*/
public void preUpdate(Object obj) ;
}
- 审计器实现
/**
* 平台审计实现类
* @author Punk
* @date 2020/05/19
*/
@Component
public class PaasAuditHandler implements AuditHandler {
/**
* 实体持久化前
* @param obj
*/
@Override
public void prePersist(Object obj) {
BaseEntity entity = (BaseEntity) obj;
this.markForCreate(entity);
}
/**
* 实体更新前
* @param obj
*/
@Override
public void preUpdate(Object obj) {
this.markForUpdate((BaseEntity)obj);
}
/**
* 普通单户创建填充信息
* @param be
*/
public void markForCreate(BaseEntity be) {
be.setCreateBy("1");
be.setCreateByName("1");
be.setCreateTime(new Date());
if (be.getDisabled() == null) {
be.setDisabled(false);
}
}
/**
* 普通单据更新填充信息
* @param be
*/
public void markForUpdate(BaseEntity be) {
be.setUpdateByName("1");
be.setUpdateTime(new Date());
}
}
- 实体监听器定义
/**
* @author Punk
* @date 2020/05/19
*/
public class PaasAuditingEntityListener {
@PrePersist
private void prePersist(Object obj) {
AuditHandler auditHandler = SpringUtil.getBean(AuditHandler.class);
auditHandler.prePersist(obj);
}
@PreUpdate
private void preUpdate(Object obj) {
AuditHandler auditHandler = SpringUtil.getBean(AuditHandler.class);
auditHandler.preUpdate(obj);
}
}
- 添加监听注解 @EnableJpaAuditing 在启动类
二、位运算
开发中,在某些场景下,需要对实体字段进行特殊的位运算。JPA本身不支持该功能,这里我们结合query-dsl实现位运算。
- 引入依赖
<!-- https://mvnrepository.com/artifact/com.querydsl/querydsl-jpa -->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>4.2.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.querydsl/querydsl-apt -->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>4.2.1</version>
</dependency>
- 位运算表达式定义
/**
* 位运算表达式定义
* @author Punk
* @date 2020/05/20
*/
public abstract class BitwiseExpressions {
/**
* 按位与
* @param arg1
* @param arg2
* @return
*/
public static NumberTemplate<Integer> bitand(Object arg1, Object arg2) {
return Expressions.numberTemplate(Integer.class, "function('bitand', {0}, {1})", arg1, arg2);
}
/**
* 按位或
* @param arg1
* @param arg2
* @return
*/
public static NumberTemplate<Integer> bitor(Object arg1, Object arg2) {
return Expressions.numberTemplate(Integer.class, "function('bitor', {0}, {1})", arg1, arg2);
}
/**
* 按位异或
* @param arg1
* @param arg2
* @return
*/
public static NumberTemplate<Integer> bitxor(Object arg1, Object arg2) {
return Expressions.numberTemplate(Integer.class, "function('bitxor', {0}, {1})", arg1, arg2);
}
}
- 数据库绑定位运算
/**
* 向数据库方言绑定位运算
* @author Punk
* @date 2020/05/20
*/
public class InjectSqlFunctionDialectResolver implements DialectResolver {
public static final String SQL_FUNCTION_BITAND = "bitand";
public static final String SQL_FUNCTION_BITOR = "bitor";
public static final String SQL_FUNCTION_BITXOR = "bitxor";
@Override
public Dialect resolveDialect(DialectResolutionInfo info) {
Dialect dialect = StandardDialectResolver.INSTANCE.resolveDialect(info);
Method method = ReflectionUtils
.findMethod(dialect.getClass(), "registerFunction", String.class, SQLFunction.class);
method.setAccessible(true);
Map<String, SQLFunction> functions = dialect.getFunctions();
if (!functions.containsKey(SQL_FUNCTION_BITAND)) {
ReflectionUtils
.invokeMethod(method, dialect, SQL_FUNCTION_BITAND, new SQLFunctionTemplate(
IntegerType.INSTANCE, "(?1 & ?2)"));
}
if (!functions.containsKey(SQL_FUNCTION_BITOR)) {
ReflectionUtils
.invokeMethod(method, dialect, SQL_FUNCTION_BITOR, new SQLFunctionTemplate(
IntegerType.INSTANCE, "(?1 | ?2)"));
}
if (!functions.containsKey(SQL_FUNCTION_BITXOR)) {
ReflectionUtils
.invokeMethod(method, dialect, SQL_FUNCTION_BITXOR, new SQLFunctionTemplate(
IntegerType.INSTANCE, "(?1 ^ ?2)"));
}
return dialect;
}
}
- 属性配置
spring:
jpa:
database-platform:
properties:
hibernate:
dialect_resolvers: com.zorpz.up.jpa.extend.InjectSqlFunctionDialectResolver
- 测试
@Repository
public interface UserRepository extends JpaRepository<User, Integer>
{
@Query(value = "select u from User u where bitand(u.flags, 1)=1")
List<User> getFlags1();
}
三、动态sql拦截
对定义的Sql进行动态拼接,比如加入租户id,部门id或者部门人员id。
- 实现StatementInteceptor
/**
* Jpa 动态sql 拦截
* @author punk
* @date 2021/02/19
*/
public class JpaInteceptor implements StatementInspector {
@Override
public String inspect(String s) {
System.out.println(s);
boolean admin = false;
if (!admin) {
boolean hasWhere = s.contains("where");
Integer orderIndex = s.indexOf("order");
if (orderIndex==-1) {
s += (hasWhere? " and " : " where ") + " status=true";
} else {
String sourceSql = s.substring(0, orderIndex);
String orderSql = s.substring(orderIndex);
sourceSql += (hasWhere? " and " : " where ") + " status=true";
s = sourceSql + " " + orderSql;
}
}
return s;
}
- 属性配置
spring:
jpa:
properties:
hibernate:
session_factory:
statement_inspector: com.zorpz.up.jpa.interceptor.JpaInteceptor