Java Spring数据JPA和hibernate分离实体传递给ManyToMany关系持久化
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23645091/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring data JPA and hibernate detached entity passed to persist on ManyToMany relationship
提问by nspessot
I am trying to persist an object that has a many-to-many relationship with other objects already persisted.
我正在尝试持久化一个与其他已经持久化的对象具有多对多关系的对象。
Here is my persisted object (they are already persisted in the db, which is a MySql):-
这是我的持久化对象(它们已经持久化在数据库中,它是一个 MySql):-
Product
产品
@Entity
@Table(name="PRODUCT")
public class Product {
private int productId;
private String productName;
private Set<Reservation> reservations = new HashSet<Reservation>(0);
@Id @GeneratedValue(strategy=GenerationType.AUTO)
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}
@Column(nullable = false)
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "products")
public Set<Reservation> getReservations() {
return reservations;
}
public void setReservations(Set<Reservation> reservations) {
this.reservations = reservations;
}
}
Here is my no persisted object, which I am trying to create
这是我正在尝试创建的非持久对象
@Entity
@Table(name = "RESERVATION")
public class Reservation {
private int reservationId;
private Set<Product> products = new HashSet<Product>(0);
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getReservationId() {
return reservationId;
}
public void setReservationId(int reservationId) {
this.reservationId = reservationId;
}
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "product_reservation", joinColumns = { @JoinColumn(name = "reservationId", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "productId",
nullable = false, updatable = false) })
public Set<Product> getProducts() {
return products;
}
public void setProducts(Set<Product> products) {
this.products = products;
}
}
This is my ReservationService
class, which receives an array of products names, look the products using the name and put them into the reservation object.
这是我的ReservationService
类,它接收一系列产品名称,使用名称查看产品并将它们放入预订对象中。
@Service
public class ReservationServiceImpl implements ReservationService {
@Autowired
private ProductDAO productDAO;
@Autowired
private ReservationDAO reservationDAO;
@Transactional
public void createReservation(String[] productNames) {
Set<Product> products = new HashSet<Product>();
for (String productName : productNames) {
Product pi = productDAO.findByProductName(productName);
products.add(pi);
}
Reservation reservation = new Reservation();
reservation.setProducts(products);
reservationDAO.save(reservation); ---> Here I am getting detached entity passed to persist
}
}
Here is my ProductDAO
interface:
这是我的ProductDAO
界面:
public interface ProductDAO extends JpaRepository<Product, Integer> {
public Product findByProductName(String productName);
}
This is my spring config file:
这是我的弹簧配置文件:
@Configuration
@PropertySource(value = { "classpath:base.properties" })
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.reservation.dao")
public class RepositoryConfig {
@Autowired
private Environment env;
@Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public PlatformTransactionManager transactionManager() {
EntityManagerFactory factory = entityManagerFactory().getObject();
return new JpaTransactionManager(factory);
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(Boolean.valueOf(env
.getProperty("hibernate.generate.ddl")));
vendorAdapter.setShowSql(Boolean.valueOf(env
.getProperty("hibernate.show_sql")));
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.hbm2ddl.auto",
env.getProperty("hibernate.hbm2ddl.auto"));
jpaProperties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource());
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.reservation.service.domain");
factory.setJpaProperties(jpaProperties);
factory.afterPropertiesSet();
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
return factory;
}
@Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.username"));
dataSource.setPassword(env.getProperty("jdbc.password"));
return dataSource;
}
}
Here is the full stack trace:
这是完整的堆栈跟踪:
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/web] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.reservation.service.domain.Product; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.reservation.service.domain.Product] with root cause org.hibernate.PersistentObjectException: detached entity passed to persist: com.reservation.service.domain.Product at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:141)
严重:servlet [dispatcher] 的 Servlet.service() 在路径为 [/web] 的上下文中抛出异常 [请求处理失败;嵌套异常是 org.springframework.dao.InvalidDataAccessApiUsageException: 分离实体传递给持久性:com.reservation.service.domain.Product; 嵌套异常是 org.hibernate.PersistentObjectException:分离实体传递给持久化:com.reservation.service.domain.Product] 根本原因 org.hibernate.PersistentObjectException:分离实体传递给持久化:com.reservation.service.domain.Product at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:141)
回答by JamesENL
I have a feeling that your annotations are slightly incorrect. Not by much though, have a look at Example 7.24 hereand see if it matches your annotations. Ignore the Collection
datatype though, as you shouldn't have any problems using a Set
. I notice that you are missing a cascade=CascadeType.ALL
on your Product
collection, but I can't work out if that is the problem or not.
我有一种感觉,您的注释有点不正确。不过不多,看看这里的示例 7.24,看看它是否与您的注释匹配。但是请忽略Collection
数据类型,因为使用Set
. 我注意到cascade=CascadeType.ALL
您的Product
收藏中缺少一个,但我无法确定这是否是问题所在。
What the actual exception is saying is that your Product
object(s) haven't actually been saved when its trying to save the collection of Product
's. That's why I think its a problem with your annotations.
实际的例外情况是,您的Product
对象在尝试保存Product
's的集合时实际上并未保存。这就是为什么我认为您的注释有问题。
Try it out and let me know if you get anywhere.
试试看,如果你有任何进展,请告诉我。
回答by Alan Hay
You need to ensure both side of the relationship are properly maintained in your code.
您需要确保在您的代码中正确维护关系的双方。
Update Reservation as below and then add the corresponding methods to Product.
更新 Reservation 如下,然后将相应的方法添加到 Product。
@Entity
@Table(name = "RESERVATION")
public class Reservation {
private int reservationId;
private Set<Product> products = new HashSet<Product>(0);
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getReservationId() {
return reservationId;
}
public void setReservationId(int reservationId) {
this.reservationId = reservationId;
}
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "product_reservation", joinColumns = { @JoinColumn(name = "reservationId", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "productId",
nullable = false, updatable = false) })
public Set<Product> getProducts() {
//force clients through our add and remove methods
return Collections.unmodifiableSet(products);
}
public void addProduct(Product product){
//avoid circular calls : assumes equals and hashcode implemented
if(! products.contains(product){
products.add(product);
//add method to Product : sets 'other side' of association
product.addReservation(this);
}
}
public void removeProduct(Product product){
//avoid circular calls: assumes equals and hashcode implemented:
if(product.contains(product){
products.remove(product);
//add method to Product: set 'other side' of association:
product.removeReservation(this);
}
}
}
And in Products:
在产品中:
public void addReservation(Reservation reservation){
//assumes equals and hashcode implemented: avoid circular calls
if(! reservations.contains(reservation){
reservations.add(reservation);
//add method to Product : sets 'other side' of association
reservation.addProduct(this);
}
}
public void removeReservation(Reservation reservation){
//assumes equals and hashcode implemented: avoid circular calls
if(! reservations.contains(reservation){
reservations.remove(reservation);
//add method to Product : sets 'other side' of association
reservation.reomveProduct(this);
}
}
Now you should be able to call save on either Product or Reservation and everything should work as expected.
现在您应该能够在 Product 或 Reservation 上调用 save 并且一切都应该按预期工作。
回答by Renato Borges
I had the same problem and solved it by removing the cascade = CascadeType.PERSIST
.
我遇到了同样的问题,并通过删除cascade = CascadeType.PERSIST
.
In your case you use CascadeType.ALL
, which is equivalent to also using the PERSIST, according to the documentation:
CascadeType.ALL
根据文档,在您的情况下,您使用,这相当于也使用 PERSIST:
Defines the set of cascadable operations that are propagated to the associated entity. The value cascade=ALL is equivalent to cascade={PERSIST, MERGE, REMOVE, REFRESH, DETACH}.
定义传播到关联实体的一组可级联操作。值cascade=ALL 等效于cascade={PERSIST, MERGE, REMOVE, REFRESH, DETACH}。
It means when you try to save the reservation on reservationDAO.save(reservation)
it will also try to persist the associated Product object. But this object is not attached to this session. So the error occur.
这意味着当您尝试在其上保存预订时reservationDAO.save(reservation)
,也会尝试保留关联的 Product 对象。但是这个对象没有附加到这个会话中。所以错误发生。
回答by Pragyan Chand
entityManager.merge()
is a good option. It will merge the detached objects in the session. No need to change any cascadeType.
entityManager.merge()
是个不错的选择。它将合并会话中的分离对象。无需更改任何级联类型。
回答by My Pham
The exception comes as hibernate trying to persist associated products when you save reservation. Persisting the products is only success if they have no id because id of Product is annotated
当您保存预订时,休眠尝试保留关联的产品时会出现异常。只有当产品没有 id 时,坚持产品才会成功,因为产品的 id 被注释
@GeneratedValue(strategy=GenerationType.AUTO)
But you got products from repository and ids are not null.
但是您从存储库中获得了产品并且 id 不为空。
There 2 options to resolve your issue:
有 2 个选项可以解决您的问题:
- remove
(cascade = CascadeType.ALL)
on products of Reservation - or remove
@GeneratedValue(strategy=GenerationType.AUTO)
on id of Product
- 移除
(cascade = CascadeType.ALL)
预订产品 - 或删除
@GeneratedValue(strategy=GenerationType.AUTO)
产品的 id
回答by karthik yadav
Remove @ CascadeType.ALL in the @ManytoMany relation, this worked for me.
删除@ManytoMany 关系中的@CascadeType.ALL,这对我有用。