在 JPA 中传递以持久保存的分离实体以进行批量插入
detached entity passed to persist for batch insert in JPA
对于以下批量插入方法,我得到这个异常"detached entity passed to persist"。你能看看这个 方法 并给我一些提示吗?
非常感谢。
如果需要,我会在这里提供实体,目前我提供关键字实体 :
public class Keyword implements Serializable {
private static final long serialVersionUID = -1429681347817644570L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="key_id")
private long keyId;
@Column(name="key_name")
private String keyName;
@ManyToOne
@JoinColumn(name="tweet_id")
private Tweet tweet;
public long getKeyId() {
return keyId;
}
public void setKeyId(long keyId) {
this.keyId = keyId;
}
public String getKeyName() {
return keyName;
}
public void setKeyName(String keyName) {
this.keyName = keyName;
}
public Tweet getTweet() {
return tweet;
}
public void setTweet(Tweet tweet) {
this.tweet = tweet;
}
}
此处推文实体:
@Entity
@Table(name="tweets")
public class Tweet implements Serializable{
@Id
@Column(name="tweet_id")
private long tweetId;
@Column(name="tweet_text")
private String tweetText;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_at")
private Date createdAt;
@Column(name="lang_code")
private String languageCode;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy="tweet")
//@JoinColumn(name="hashtag_id")
private List<Hashtag> hashtags;
@OneToMany(mappedBy="tweet")
//@JoinColumn(name="url_id")
private List<Url> urls;
public List<Keyword> getKeywords() {
return keywords;
}
public void setKeywords(List<Keyword> keywords) {
this.keywords = keywords;
}
@OneToMany(mappedBy="tweet")
//@JoinColumn(name="url_id")
private List<Keyword> keywords;
public long getTweetId() {
return tweetId;
}
public void setTweetId(long tweetId) {
this.tweetId = tweetId;
}
public String getTweetText() {
return tweetText;
}
public void setTweetText(String tweetText) {
this.tweetText = tweetText;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getLanguageCode() {
return languageCode;
}
public void setLanguageCode(String languageCode) {
this.languageCode = languageCode;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<Hashtag> getHashtags() {
return hashtags;
}
public void setHashtags(List<Hashtag> hashtags) {
this.hashtags = hashtags;
}
public List<Url> getUrls() {
return urls;
}
public void setUrls(List<Url> urls) {
this.urls = urls;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (tweetId ^ (tweetId >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tweet other = (Tweet) obj;
if (tweetId != other.tweetId)
return false;
return true;
}
这里 Url 实体:
@Entity
@Table(name="tweet_url")
public class Url implements Serializable{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="url_id")
private int urlId;
@Column(name="url")
private String url;
@ManyToOne
@JoinColumn(name="tweet_id",referencedColumnName="tweet_id")
private Tweet tweet;
public int getUrlId() {
return urlId;
}
public void setUrlId(int urlId) {
this.urlId = urlId;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Tweet getTweet() {
return tweet;
}
public void setTweet(Tweet tweet) {
this.tweet = tweet;
}
这是 hashtag 实体:
@Entity
@Table(name="tweet_hashtag")
public class Hashtag implements Serializable{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="hashtag_id")
private int hashtagId;
@Column(name="hashtag")
private String hashtag;
@ManyToOne
@JoinColumn(name="tweet_id",referencedColumnName="tweet_id")
private Tweet tweet;
public int getHashtagId() {
return hashtagId;
}
public void setHashtagId(int hashtagId) {
this.hashtagId = hashtagId;
}
public String getHashtag() {
return hashtag;
}
public void setHashtag(String hashtag) {
this.hashtag = hashtag;
}
public Tweet getTweet() {
return tweet;
}
public void setTweet(Tweet tweet) {
this.tweet = tweet;
}
和方法:
public void batchInsert(List<Keyword> results) throws HibernateException {
// chekeywordck if key exists
// try {
em=RunQuery.emf.createEntityManager();
em.getTransaction().begin();
for(Keyword result:results)
{
try{
em.persist(result.getTweet().getUser());
}
catch(ConstraintViolationException ce)
{
System.out.print("duplicated insert catched");
}
try{
em.persist(result.getTweet());
}
catch(ConstraintViolationException ce)
{
System.out.print("duplicated insert catched");
}
if(result.getTweet().getHashtags()!=null)
for(Hashtag hashtag:result.getTweet().getHashtags())
em.persist(hashtag);
if(result.getTweet().getUrls()!=null)
for(Url url:result.getTweet().getUrls())
em.persist(url);
em.persist(result);
em.flush();
em.clear();
//when I put these two line out of this loop, it still is the same.
}
em.getTransaction().commit();
// }
}
这里是个例外:
Exception in thread "Thread-3" javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: model.twitter.entities.Url
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1187)
at model.service.QueryResultService.batchInsert(QueryResultService.java:74)
at controller.ResultsController.save(ResultsController.java:125)
at controller.ResultsController.parse(ResultsController.java:89)
at main.TwitterStreamConsumer.run(TwitterStreamConsumer.java:41)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: model.twitter.entities.Url
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:811)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181)
... 5 more
对于所有持久调用,请尝试改用此方法:
if(result.getTweet().getUser().getId() == null) {
em.persist(result.getTweet().getUser());
} else {
result.getTweet().setUser(em.merge(result.getTweet().getUser()));
}
if(result.getTweet().getId() == null) {
em.persist(result.getTweet());
} else {
result.setTweet(em.merge(result.getTweet()));
}
if(result.getId() == null) {
em.persist(result);
} else {
result = em.merge(result);
}
回答您的问题:您的模型定义了 Tweet 和 URL之间的一对多关系,没有任何级联。当您传递 Tweet 实例以进行持久化时,URL 对象尚未保存,您的模型不会强制 Tweet 将持久化操作级联到 URL 实例。因此它不能与他们建立关系。
Cascading 告诉 hibernate,如何对相关实体执行 DB 操作。
您可以指示它 pass/cascade 对相关实体的持久化操作,以级联所有操作或操作数组。
也就是说,如果您修改与级联信息的关系,您的问题(其中 1 个)可能会得到解决:
@OneToMany(mappedBy="tweet", cascade={CascadeType.PERSIST})
private List<Url> urls;
但是您的示例指出了其他可能的问题,我鼓励您花更多时间阅读 Hibernate ORM 文档并在关系较少的示例模型上进行练习。
一个明显的问题似乎是缺乏对关系所有者概念的理解。
例如,在你的 Tweet-to-Url 关系中,URL 是关系所有者(负责管理关系,例如通过外键管理 link)
请咨询 hibernate docs or one of hundreds of similar questions here on SO 了解更多信息。
根据您填充数据的方式,您可能会 运行 陷入约束问题,或者您的实体不会 link 在一起,因为您没有保存拥有方。
同样使用 try/catch 来检测约束违规是一种非常糟糕的检测重复条目的方法。 ConstraintViolationException 可能有很多原因,您得到它们的原因与上述关系映射问题有关。
ORM 是一门复杂的学科,从较小的示例开始着手,在转向更具挑战性的模型之前尝试理解框架机制是非常有益的。祝你好运
对于以下批量插入方法,我得到这个异常"detached entity passed to persist"。你能看看这个 方法 并给我一些提示吗?
非常感谢。
如果需要,我会在这里提供实体,目前我提供关键字实体 :
public class Keyword implements Serializable {
private static final long serialVersionUID = -1429681347817644570L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="key_id")
private long keyId;
@Column(name="key_name")
private String keyName;
@ManyToOne
@JoinColumn(name="tweet_id")
private Tweet tweet;
public long getKeyId() {
return keyId;
}
public void setKeyId(long keyId) {
this.keyId = keyId;
}
public String getKeyName() {
return keyName;
}
public void setKeyName(String keyName) {
this.keyName = keyName;
}
public Tweet getTweet() {
return tweet;
}
public void setTweet(Tweet tweet) {
this.tweet = tweet;
}
}
此处推文实体:
@Entity
@Table(name="tweets")
public class Tweet implements Serializable{
@Id
@Column(name="tweet_id")
private long tweetId;
@Column(name="tweet_text")
private String tweetText;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_at")
private Date createdAt;
@Column(name="lang_code")
private String languageCode;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy="tweet")
//@JoinColumn(name="hashtag_id")
private List<Hashtag> hashtags;
@OneToMany(mappedBy="tweet")
//@JoinColumn(name="url_id")
private List<Url> urls;
public List<Keyword> getKeywords() {
return keywords;
}
public void setKeywords(List<Keyword> keywords) {
this.keywords = keywords;
}
@OneToMany(mappedBy="tweet")
//@JoinColumn(name="url_id")
private List<Keyword> keywords;
public long getTweetId() {
return tweetId;
}
public void setTweetId(long tweetId) {
this.tweetId = tweetId;
}
public String getTweetText() {
return tweetText;
}
public void setTweetText(String tweetText) {
this.tweetText = tweetText;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getLanguageCode() {
return languageCode;
}
public void setLanguageCode(String languageCode) {
this.languageCode = languageCode;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<Hashtag> getHashtags() {
return hashtags;
}
public void setHashtags(List<Hashtag> hashtags) {
this.hashtags = hashtags;
}
public List<Url> getUrls() {
return urls;
}
public void setUrls(List<Url> urls) {
this.urls = urls;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (tweetId ^ (tweetId >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tweet other = (Tweet) obj;
if (tweetId != other.tweetId)
return false;
return true;
}
这里 Url 实体:
@Entity
@Table(name="tweet_url")
public class Url implements Serializable{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="url_id")
private int urlId;
@Column(name="url")
private String url;
@ManyToOne
@JoinColumn(name="tweet_id",referencedColumnName="tweet_id")
private Tweet tweet;
public int getUrlId() {
return urlId;
}
public void setUrlId(int urlId) {
this.urlId = urlId;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Tweet getTweet() {
return tweet;
}
public void setTweet(Tweet tweet) {
this.tweet = tweet;
}
这是 hashtag 实体:
@Entity
@Table(name="tweet_hashtag")
public class Hashtag implements Serializable{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="hashtag_id")
private int hashtagId;
@Column(name="hashtag")
private String hashtag;
@ManyToOne
@JoinColumn(name="tweet_id",referencedColumnName="tweet_id")
private Tweet tweet;
public int getHashtagId() {
return hashtagId;
}
public void setHashtagId(int hashtagId) {
this.hashtagId = hashtagId;
}
public String getHashtag() {
return hashtag;
}
public void setHashtag(String hashtag) {
this.hashtag = hashtag;
}
public Tweet getTweet() {
return tweet;
}
public void setTweet(Tweet tweet) {
this.tweet = tweet;
}
和方法:
public void batchInsert(List<Keyword> results) throws HibernateException {
// chekeywordck if key exists
// try {
em=RunQuery.emf.createEntityManager();
em.getTransaction().begin();
for(Keyword result:results)
{
try{
em.persist(result.getTweet().getUser());
}
catch(ConstraintViolationException ce)
{
System.out.print("duplicated insert catched");
}
try{
em.persist(result.getTweet());
}
catch(ConstraintViolationException ce)
{
System.out.print("duplicated insert catched");
}
if(result.getTweet().getHashtags()!=null)
for(Hashtag hashtag:result.getTweet().getHashtags())
em.persist(hashtag);
if(result.getTweet().getUrls()!=null)
for(Url url:result.getTweet().getUrls())
em.persist(url);
em.persist(result);
em.flush();
em.clear();
//when I put these two line out of this loop, it still is the same.
}
em.getTransaction().commit();
// }
}
这里是个例外:
Exception in thread "Thread-3" javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: model.twitter.entities.Url
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1187)
at model.service.QueryResultService.batchInsert(QueryResultService.java:74)
at controller.ResultsController.save(ResultsController.java:125)
at controller.ResultsController.parse(ResultsController.java:89)
at main.TwitterStreamConsumer.run(TwitterStreamConsumer.java:41)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: model.twitter.entities.Url
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:811)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181)
... 5 more
对于所有持久调用,请尝试改用此方法:
if(result.getTweet().getUser().getId() == null) {
em.persist(result.getTweet().getUser());
} else {
result.getTweet().setUser(em.merge(result.getTweet().getUser()));
}
if(result.getTweet().getId() == null) {
em.persist(result.getTweet());
} else {
result.setTweet(em.merge(result.getTweet()));
}
if(result.getId() == null) {
em.persist(result);
} else {
result = em.merge(result);
}
回答您的问题:您的模型定义了 Tweet 和 URL之间的一对多关系,没有任何级联。当您传递 Tweet 实例以进行持久化时,URL 对象尚未保存,您的模型不会强制 Tweet 将持久化操作级联到 URL 实例。因此它不能与他们建立关系。 Cascading 告诉 hibernate,如何对相关实体执行 DB 操作。 您可以指示它 pass/cascade 对相关实体的持久化操作,以级联所有操作或操作数组。
也就是说,如果您修改与级联信息的关系,您的问题(其中 1 个)可能会得到解决:
@OneToMany(mappedBy="tweet", cascade={CascadeType.PERSIST})
private List<Url> urls;
但是您的示例指出了其他可能的问题,我鼓励您花更多时间阅读 Hibernate ORM 文档并在关系较少的示例模型上进行练习。
一个明显的问题似乎是缺乏对关系所有者概念的理解。 例如,在你的 Tweet-to-Url 关系中,URL 是关系所有者(负责管理关系,例如通过外键管理 link) 请咨询 hibernate docs or one of hundreds of similar questions here on SO 了解更多信息。 根据您填充数据的方式,您可能会 运行 陷入约束问题,或者您的实体不会 link 在一起,因为您没有保存拥有方。 同样使用 try/catch 来检测约束违规是一种非常糟糕的检测重复条目的方法。 ConstraintViolationException 可能有很多原因,您得到它们的原因与上述关系映射问题有关。 ORM 是一门复杂的学科,从较小的示例开始着手,在转向更具挑战性的模型之前尝试理解框架机制是非常有益的。祝你好运