我需要为我的应用程序管理的所有实体添加软删除功能。为了实现这一点,我创建了一个带有已删除字段的基本实体类(MappedSuperclass),如下所示:
@MappedSuperclass
public abstract class BaseVersionableEntity {
@Id
@Basic(optional = false)
@NotNull
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID", nullable = false)
protected Long id;
@Version
@Column(name = "VERSION", nullable = false)
protected Long version;
@Basic
@Column(name = "DELETED")
protected boolean deleted;
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
// equals/hashCode
}
通过使用hibernate的拦截器机制,我配置了一个软删除拦截器,以便捕获所有hibernate执行的查询:
public class SoftSelectInterceptor extends EmptyInterceptor{
private static final long serialVersionUID = 1L;
@Override
public String onPrepareStatement(String sql) {
if(sql.startsWith("select")){
sql = sql.replace("where ","where deleted=0 and ");
}
return super.onPrepareStatement(sql);
}
}
我在拦截器配置下面添加了 persistence.xml 文件
<property name="hibernate.ejb.interceptor" value="com.my.organization.SoftSelectInterceptor"/>
请注意,还有一个 SoftDeleteInterceptor 。以同样的方式工作。
当我执行此代码时,hibernate会在 onPeraparedStatement 方法中捕获select语句调用,但它会在表字段前面添加一个表别名。也就是说,如果表名是 reportTemplate ,则hibernate会为此表提供 reportTemplate0 别名。所以我的替换语句需要如下所示:
sql = sql.replace("where ","where reportTemplate0.deleted=0 and ");
我可以解析sql字符串并尝试推断出正确的表别名。但是在复杂的sql语句中(当多次使用同一个实体时)很难推断出来。
我想知道是否有更好的方法来做到这一点。
答案 0 :(得分:1)
我看到三个选项:
使用Hibernate Filters。主要缺点:您必须为每个实体附加过滤器。
@Entity
// The FilterDef can be declared at package level as well:
@FilterDef(name="alive", defaultCondition="deleted = 0")
// Turn on the filter for our entity:
@Filter(name="alive")
public class MyEntity { ... }
使用SQL解析器(有足够的数据),修改解析后的结果并重新创建SQL。主要缺点:有足够的不那么琐碎的陈述(包括子选择)来修改。您必须确保仅将限制添加到受影响的表中。
答案 1 :(得分:0)
您可以将可删除实体映射到数据库视图,这些数据库视图使用过滤条件(deleted = 0
)从原始表中进行选择。
修改强>
不幸的是,按照你想要的方式做到这一点是不可能的。
1)无法准确预测别名的外观,特别是对于具有多个连接和子查询的复杂查询。即使您分析源代码,它也不是公共API的一部分,因此可能会发生变化。
但是,如果你决定采取这种努力(分析Hibernate源代码进行查询解析和SQL生成),请记住,你可能整个夏天只会这样做,因为它是一个复杂而庞大的代码库和很少有非常重要的功能请求(如left joins for non-associated entities)等待多年重构。
2)没有where
子句的查询(和子查询)不会排除已删除的行(因为您的替换将找不到匹配项):
select c from MyEntity;
3) 字母组合但仍有可能的可能性较小,其中可能是标识符或文本常量的一部分,而您的替换会使查询无效。假设有人向Trip
实体添加了两个字段:fromWhere
和toWhere
。也许不是一个好的命名例子,但你明白了。
你可以通过仅替换整个where
单词的出现来解决它,但是想象一下这样的查询:
select ... from ... where aColumn = 'A constant with the where word in it'