从实体拥有与他人关系的模型开始 实体。第二个实体限制可以在属性上设置的值 它映射了这种关系。让我用一个例子更好地说明这一点。
+----------+
| Box | +------------+
+----------+ | BoxType |
|PK Box | +------------+
|FK BoxType| * -----> 1 | PK BoxType |
+----------+ +------------+
这些是相应的实体(实际上,实际的实现有所不同 从这个例子中可以看出,我认为这对我来说是必要的 我想要的是什么。
@Entity @Table(name = "Box") public class Box {
@Id @Column(name = "Box") private Integer box;
@ManyToOne @JoinColumn(
name = "BoxType",
referencedColumnName = "BoxType"
) private BoxType boxType;
// getters, setters
}
@Entity @Table(name = "BoxType") public class BoxType {
public static enum BoxTypeEnum {
SMALL, AVERAGE, BIG;
}
@Id @Column(name = "BoxType") private String boxType;
public BoxTypeEnum getBoxType() {
return BoxTypeEnum.valueOf(boxType);
}
public void setBoxType(BoxTypeEnum boxType) {
this.boxType = boxType.name();
}
}
值SMALL
,AVERAGE
和BIG
已保存在BoxType
中
应用程序运行前的表。让我们假设OpenJPA用于
持久性提供者。如果尝试持久化Box实例
返回异常,显示boxType
属性保持不受管理
BoxType
对象。
<openjpa-2.3.0-nonfinal-1540826-r422266:1542644 nonfatal user error>
org.apache.openjpa.persistence.InvalidStateException:
Encountered unmanaged object "BoxType@bdb3dc" in life cycle state
unmanaged while cascading persistence via field "Box.boxType" during
flush. However, this field does not allow cascade persist. You cannot
flush unmanaged objects or graphs that have persistent associations to
unmanaged objects.
Suggested actions:
a) Set the cascade attribute for this field to CascadeType.PERSIST or
CascadeType.ALL (JPA annotations) or "persist" or "all" (JPA
orm.xml),
b) enable cascade-persist globally,
c) manually persist the related field value prior to flushing.
d) if the reference belongs to another context, allow reference to it
by setting StoreContext.setAllowReferenceToSiblingContext().
如果关系标记为级联,则相反,例外情况是
返回,显示应用程序正在尝试的BoxType
实例
数据库中已存在持久性。
@ManyToOne @JoinColumn(
name = "BoxType",
referencedColumnName = "BoxType",
cascade = CascadeType.PERSIST
) private BoxType boxType;
例外。
<openjpa-2.3.0-nonfinal-1540826-r422266:1542644 nonfatal store error>
org.apache.openjpa.persistence.EntityExistsException:
An object of type "BoxType" with oid "SMALL" already exists in this
context; another cannot be persisted.
就我而言,我有两种选择:
BoxType
并更新Box
上的参考。这个选项
需要对数据库进行额外查询。box.setBoxType(entityManager.find(
BoxType.class, box.getBoxType().getBoxType().name()))
entityManager.persist(box);
PeparedStatement preparedStatement =
connection.prepareStatement(
"INSERT INTO Box(Box, BoxType) VALUES (?, ?);");
preparedStatement.setInt(1, box.getBox());
preparedStatement.setString(2, box.getBoxType().getBoxType().name());
preparedStatement.execute();
目前我总是实施第一个选项,除此之外
效率低下。我总是将类似BoxType的实体标记为@Cacheable
跳跃它
将阻止应用程序进行额外查询。
我的问题是:¿有没有办法在不使用JPA的情况下持久保存实体
得到关系的另一面?,¿有没有其他方法
在这种情况下坚持一个实体?,¿@Cacheable
确实阻止了
额外的查询?,我错过了什么吗?
StackOverflow上有相关问题(this one 是一个很好的例子)但是他们似乎都没有关注同一个问题。
我确认缓存BoxType
会阻止其他SELECT
查询。我有
带BoxType
的注释@Cacheable(true)
并添加
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
到
持久性单位。接下来我发布一个日志部分,显示第一次a
某些类型的Box
持久存在相应的BoxType
实例
合并,而第二次只执行INSERT
。
84485 boxPU TRACE [http-bio-8080-exec-3] openjpa.jdbc.SQL - <t
31743778, conn 30174353> executing prepstmnt 604724
SELECT t0.BOX_TYPE
FROM BOX_TYPE t0
WHERE t0.BOX_TYPE = ?
[params=?]
84765 boxPU TRACE [http-bio-8080-exec-3] openjpa.jdbc.SQL - <t
31743778, conn 30174353> [280 ms] spent
84846 boxPU TRACE [http-bio-8080-exec-3] openjpa.jdbc.SQL - <t
31743778, conn 30174353> executing prepstmnt 1210409
INSERT INTO BOX (BOX, BOX_TYPE)
VALUES (?, ?)
[params=?, ?]
84874 boxPU TRACE [http-bio-8080-exec-3] openjpa.jdbc.SQL - <t
31743778, conn 30174353> [28 ms] spent
165033 boxPU TRACE [http-bio-8080-exec-5] openjpa.jdbc.SQL - <t
20406142, conn 3709916> executing prepstmnt 25081694
INSERT INTO BOX (BOX, BOX_TYPE)
VALUES (?, ?)
[params=?, ?]
165034 boxPU TRACE [http-bio-8080-exec-5] openjpa.jdbc.SQL - <t
20406142, conn 3709916> [1 ms] spent
答案 0 :(得分:1)
不是你的问题的答案,但可能会解决它:
不要使用类似实体的枚举。在表中持久化枚举值并使用此表没有任何附加值。你可以简单地使用枚举本身。您可以删除BoxType
课程,只保留BoxTypeEnum
,然后您可以将其重命名为BoxType
。你的Box类看起来像这样:
@Enumerated(EnumType.STRING)
private BoxType boxType;
或不重命名:
@Enumerated(EnumType.STRING)
private BoxTypeEnum boxType;
编辑:如果您希望保持实体不变,并且能够保留新的Box,则必须首先合并BoxType
实例并将合并后的实例附加到框中。由于它已经设置了id
,因此持久性提供程序假定它是一个已经存在的实例。这就是持续级联失败的原因。你可能会这样解决它:
BoxType mergedAverageBoxType = em.merge(averageBoxType);
box.setBoxType(mergedAverageBoxType);
em.persist(box);
然而,请再次考虑BoxType
类的设计。如果要通过将框架类型映射到表格来使外部框架类型可扩展,请不要使用enum
,因为您不能拥有BoxType
id
的{{1}}实例。列于enum
。
但是,如果您只想从源代码中创建可扩展的框类型列表,则表格是多余的。