JPA常见使用小结

这篇博客介绍了如何在JPA中实现实体的审计功能,包括在保存和更新时自动记录操作人和时间。同时,展示了如何结合QueryDSL实现位运算,如按位与、按位或和按位异或,并且提供了数据库方言的绑定。此外,还讨论了通过StatementInterceptor动态拼接SQL,例如根据权限过滤结果。
摘要由CSDN通过智能技术生成

一、JPA 审计

实体保存、更新时要保留操作人以及操作时间。
  1. 实体基类
/**
 * @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;
  }

}
  1. 审计器接口
/**
 * 平台审计处理类
 * @author Punk
 * @date 2020/05/19
 */
public interface AuditHandler {

    /**
     * 保存前
     * @param obj
     */
    public void prePersist(Object obj) ;

    /**
     * 更新前
     * @param obj
     */
    public void preUpdate(Object obj) ;
}
  1. 审计器实现
/**
 * 平台审计实现类
 * @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());
  }
}
  1. 实体监听器定义
/**
 * @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);
    }
}

  1. 添加监听注解 @EnableJpaAuditing 在启动类

二、位运算

开发中,在某些场景下,需要对实体字段进行特殊的位运算。JPA本身不支持该功能,这里我们结合query-dsl实现位运算。
  1. 引入依赖
    <!-- 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>
  1. 位运算表达式定义
/**
 *  位运算表达式定义
 * @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);
    }
}

  1. 数据库绑定位运算
/**
 *  向数据库方言绑定位运算
 * @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;
    }
}
  1. 属性配置
spring:
  jpa:
    database-platform:
    properties:
      hibernate:
        dialect_resolvers: com.zorpz.up.jpa.extend.InjectSqlFunctionDialectResolver
  1. 测试
@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。

  1. 实现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;
  }


  1. 属性配置
spring:
  jpa:
    properties:
      hibernate:
        session_factory:
          statement_inspector: com.zorpz.up.jpa.interceptor.JpaInteceptor
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值