我们有一个类,在我们处理从一个实体到另一个实体的值复制的项目中,将其命名为AttributeUpdater。核心方法遍历实体的属性,并按照指定将它们复制到第二个实体中。在该循环中,AttributeUpdater将所有报告收集到一个漂亮的列表中,用于最终的日志记录目的,这些报告包含有关复制过程中覆盖的值的信息。如果值被覆盖的旧实体从未持久化到数据库中,则会删除此列表,因为在这种情况下,您只会覆盖默认值和被视为冗余的日志记录。在伪Java代码中:
public class AttributeUpdater {
public static CopyResult updateAttributes(Entity source, Entity target, String[] attributes) {
List<CopyReport> reports = new ArrayList<CopyReport>();
for(String attribute : attributes) {
reports.add(copy(source, target, attribute));
}
if(target.isNotPersisted()) {
reports.clear();
}
return new CopyResult(reports);
}
}
现在有人顿悟,在一种情况下,即使实体尚未持久化,报告也很重要。如果我可以在方法签名中添加另一个参数,那就没什么大不了的了,但由于类的实际结构和所需的折射量,这在某种程度上是不可选择的。由于该方法是静态的,我想到的唯一其他解决方案是添加一个标志作为静态字段,并在函数调用之前设置它。
public class AttributeUpdater {
public static final ThreadLocal<Boolean> isDeletionEnabled = new ThreadLocal<Boolean> {
@Override protected Boolean initialValue() {
return Boolean.TRUE;
}
public static Boolean getDeletionEnabled() { return isDeletionEnabled.get(); }
public static void setDeletionEnabled(Boolean b) { isDeletionEnabled.set(b); }
public static CopyResult updateAttributes(Entity source, Entity target, String[] attributes) {
List<CopyReport> reports = new ArrayList<CopyReport>();
for(String attribute : attributes) {
reports.add(copy(source, target, attribute));
}
if(isDeletionEnabled.get() && target.isNotPersisted()) {
reports.clear();
}
return new CopyResult(reports);
}
}
ThreadLocal是一个用于线程安全的容器。这种解决方案在起作用的同时,至少对我来说有一个主要缺点:对于所有其他假设报告已删除的方法,现在无法保证这些报告将按预期删除。再次强调,折射不是一种选择。所以我想出了这个:
公共类AttributeUpdater{
private static final ThreadLocal<Boolean> isDeletionEnabled = new ThreadLocal<Boolean> {
@Override protected Boolean initialValue() {
return Boolean.TRUE;
}
public static Boolean getDeletionEnabled() { return isDeletionEnabled.get(); }
public static void disableDeletionForNextCall() { isDeletionEnabled.set(Boolean.FALSE); }
public static CopyResult updateAttributes(Entity source, Entity target, String[] attributes) {
List<CopyReport> reports = new ArrayList<CopyReport>();
for(String attribute : attributes) {
reports.add(copy(source, target, attribute));
}
if(isDeletionEnabled.get() && target.isNotPersisted()) {
reports.clear();
}
isDeletionEnabled.set(Boolean.TRUE);
return new CopyResult(reports);
}
}
通过这种方式,我可以保证对于旧代码,函数将始终像更改前一样工作。这个解决方案的缺点是,尤其是对于嵌套实体,我将大量访问ThreadLocalContainer——对其中一个进行迭代意味着对每个嵌套元素调用disableDeletitionForNextCall()。此外,由于该方法总体上被调用了很多,因此存在有效的性能问题。
TL;DR:看看伪Java源代码。第一个是旧代码,第二个和第三个是允许禁用删除的不同尝试。无法将参数添加到方法签名中。
是否有可能确定哪种解决方案更好,或者这只是一个哲学问题?或者这个问题还有更好的解决方案吗?
决定哪种解决方案在性能方面更好的明显方法是对此进行基准测试。由于两种解决方案都访问线程局部变量(至少用于读取),我怀疑它们之间的差异会太大。你也许可以这样组合它们:
if(!isDeletionEnabled.get())
isDeletionEnabled.set(Boolean.TRUE);
else if (target.isNotPersisted())
reports.clear();
在这种情况下,您将获得第二种解决方案(保证重置标志)的好处,而不会出现不必要的写入。
我怀疑会有多大的实际差异。如果运气好的话,HotSpot JVM会将线程本地变量编译成一些不错的本地代码,虽然我在这方面没有实际经验,但它不会带来太多的性能损失。