Pau*_*ald 211 java hibernate jpa
我有一个JPA持久化对象模型,它包含多对一关系:一个帐户有许多交易.交易有一个帐户.
这是代码的片段:
@Entity
public class Transaction {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne(cascade = {CascadeType.ALL},fetch= FetchType.EAGER)
private Account fromAccount;
....
@Entity
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToMany(cascade = {CascadeType.ALL},fetch= FetchType.EAGER, mappedBy = "fromAccount")
private Set<Transaction> transactions;
Run Code Online (Sandbox Code Playgroud)
我能够创建一个Account对象,向其添加事务,并正确地持久保存Account对象.但是,当我创建一个事务,使用现有已经存在的帐户,并持久化事务时,我得到一个例外:
if (account.getId()!=null) {
account = entityManager.merge(account);
}
Transaction transaction = new Transaction(account,"other stuff");
// the below fails with a "detached entity" message. why?
entityManager.persist(transaction);
Run Code Online (Sandbox Code Playgroud)
因此,我能够持久保存包含交易的账户,但不能持有具有账户的交易.我认为这是因为帐户可能没有附加,但这段代码仍然给了我同样的例外:
@Entity
public class Transaction {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne(cascade = {CascadeType.ALL},fetch= FetchType.EAGER)
private Account fromAccount;
....
@Entity
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToMany(cascade = {CascadeType.ALL},fetch= FetchType.EAGER, mappedBy = "fromAccount")
private Set<Transaction> transactions;
Run Code Online (Sandbox Code Playgroud)
如何正确保存与已持久的Account对象关联的事务?
och*_*lad 242
解决方案很简单,只需使用CascadeType.MERGE而不是CascadeType.PERSIST或CascadeType.ALL.
我遇到了同样的问题并CascadeType.MERGE为我工作过.
我希望你排序.
Sym*_*Sym 119
这是典型的双向一致性问题.在此链接以及此链接中对此进行了详细讨论.
根据前两个链接中的文章,您需要在双向关系的两侧修复您的setter.One侧的示例设置器位于此链接中.
许多方面的示例设置器在此链接中.
在更正了setter之后,您希望将Entity访问类型声明为"Property".声明"属性"访问类型的最佳做法是将所有注释从成员属性移动到相应的getter.值得注意的是,不要在实体类中混合使用"Field"和"Property"访问类型,否则JSR-317规范不会定义行为.
Vla*_*cea 20
因此,您需要@CascadeType.ALL从@ManyToOne关联中删除。子实体不应级联到父关联。只有父实体应该级联到子实体。
@ManyToOne(fetch= FetchType.LAZY)
Run Code Online (Sandbox Code Playgroud)
请注意,我将该fetch属性设置为FetchType.LAZY因为急切获取对性能非常不利。
每当您有双向关联时,您都需要使用父实体中的addChild和removeChild方法同步双方:
public void addTransaction(Transaction transaction) {
transcations.add(transaction);
transaction.setAccount(this);
}
public void removeTransaction(Transaction transaction) {
transcations.remove(transaction);
transaction.setAccount(null);
}
Run Code Online (Sandbox Code Playgroud)
Zds*_*Zds 14
使用合并是有风险和棘手的,因此在您的情况下这是一个肮脏的解决方法.至少需要记住,当您传递实体对象进行合并时,它会停止附加到事务,而是返回一个新的,现在附加的实体.这意味着如果任何人仍然拥有旧的实体对象,则对它的更改会被默认忽略并在提交时丢弃.
您没有在此处显示完整代码,因此我无法仔细检查您的交易模式.达到这种情况的一种方法是,如果在执行合并并保持活动时没有活动的事务.在这种情况下,持久性提供程序应为您执行的每个JPA操作打开一个新事务,并在调用返回之前立即提交并关闭它.如果是这种情况,合并将在第一个事务中运行,然后在merge方法返回之后,事务完成并关闭,并且返回的实体现在已分离.然后,它下面的持久化将打开第二个事务,并尝试引用已分离的实体,并给出异常.除非您非常了解自己在做什么,否则请始终将代码包装在事务中.
使用容器管理的事务,它看起来像这样.请注意:这假设该方法位于会话bean中并通过本地或远程接口调用.
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void storeAccount(Account account) {
...
if (account.getId()!=null) {
account = entityManager.merge(account);
}
Transaction transaction = new Transaction(account,"other stuff");
entityManager.persist(account);
}
Run Code Online (Sandbox Code Playgroud)
Eug*_*bun 11
从子实体中删除级联Transaction,它应该是:
@Entity class Transaction {
@ManyToOne // no cascading here!
private Account account;
}
Run Code Online (Sandbox Code Playgroud)
(FetchType.EAGER可以删除,也可以将其删除@ManyToOne)
就这样!
为什么?通过在子实体上说“级联所有”,Transaction您需要将每个数据库操作传播到父实体Account。如果您这样做persist(transaction),persist(account)也会被调用。
但是只有临时(新)实体可以传递给persist(Transaction在这种情况下)。分离(或其他非暂态)状态可能不会(Account在这种情况下,因为它已经存在于DB中)。
因此,您将获得异常“已将分离的实体传递给持久对象”。该Account实体的意思!不是Transaction你打电话persist。
您通常不希望从孩子传播到父母。不幸的是,书中有很多代码示例(即使是很好的示例)也可以通过网络找到,它们确实做到了。我不知道为什么?也许有时候只是一遍又一遍地复制而没有太多的思考...
猜猜如果您remove(transaction)在@ManyToOne中仍称“层叠所有”会发生什么?的account(顺便说一句,与所有其他交易!)将被从数据库以及删除。但这不是您的意图,对吗?
可能在这种情况下,您account使用合并逻辑获取了对象,并persist用于持久化新对象,如果层次结构具有已持久化的对象,它将会抱怨.你应该saveOrUpdate在这种情况下使用,而不是persist.
一个老问题,但最近遇到了同样的问题。在这里分享我的经验。
实体
@Data
@Entity
@Table(name = "COURSE")
public class Course {
@Id
@GeneratedValue
private Long id;
}
Run Code Online (Sandbox Code Playgroud)
保存实体 (JUnit)
Course course = new Course(10L, "testcourse", "DummyCourse");
testEntityManager.persist(course);
Run Code Online (Sandbox Code Playgroud)
使固定
Course course = new Course(null, "testcourse", "DummyCourse");
testEntityManager.persist(course);
Run Code Online (Sandbox Code Playgroud)
结论:如果实体类具有主键(id)@GenerateValue,那么请确保您没有为主键(id)传递值
在你的实体定义,你不指定@JoinColumn的Account加入到Transaction。你会想要这样的东西:
@Entity
public class Transaction {
@ManyToOne(cascade = {CascadeType.ALL},fetch= FetchType.EAGER)
@JoinColumn(name = "accountId", referencedColumnName = "id")
private Account fromAccount;
}
Run Code Online (Sandbox Code Playgroud)
编辑:好吧,我想如果您@Table在课堂上使用注释会很有用。呵呵。:)
即使您的注释已正确声明以正确管理一对多关系,您仍然可能会遇到这种精确的异常。将新的子对象添加Transaction到附加数据模型时,您需要管理主键值 -除非您不应该这样做。如果您在调用之前为按如下方式声明的子实体提供主键值persist(T),您将遇到此异常。
@Entity
public class Transaction {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
....
Run Code Online (Sandbox Code Playgroud)
在这种情况下,注释声明数据库将在插入时管理实体主键值的生成。自己提供一个(例如通过 Id 的 setter)会导致此异常。
或者,但实际上是相同的,此注释声明会导致相同的异常:
@Entity
public class Transaction {
@Id
@org.hibernate.annotations.GenericGenerator(name="system-uuid", strategy="uuid")
@GeneratedValue(generator="system-uuid")
private Long id;
....
Run Code Online (Sandbox Code Playgroud)
id因此,当应用程序代码已被管理时,请勿在应用程序代码中设置该值。
如果没有任何帮助并且您仍然收到此异常,请检查您的equals()方法 - 并且不要在其中包含子集合。特别是如果您具有嵌入式集合的深层结构(例如,A 包含 B,B 包含 C,等等)。
在以下示例中Account -> Transactions:
public class Account {
private Long id;
private String accountName;
private Set<Transaction> transactions;
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Account))
return false;
Account other = (Account) obj;
return Objects.equals(this.id, other.id)
&& Objects.equals(this.accountName, other.accountName)
&& Objects.equals(this.transactions, other.transactions); // <--- REMOVE THIS!
}
}
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,从equals()支票中删除交易。这是因为休眠将暗示您不会尝试更新旧对象,而是在更改子集合上的元素时传递一个新对象以持久化。
当然,此解决方案并不适合所有应用程序,您应该仔细设计要包含在equals和hashCode方法中的内容。
我的基于 Spring Data JPA 的答案:我只是@Transactional在我的外部方法中添加了一个注释。
子实体立即分离,因为没有活动的 Hibernate Session 上下文。提供 Spring (Data JPA) 事务可确保存在 Hibernate Session。
https://vladmihalcea.com/a-beginners-guide-to-jpa-hibernate-entity-state-transitions/
| 归档时间: |
|
| 查看次数: |
366555 次 |
| 最近记录: |