Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring data JPA and hibernate detached entity passed to persist on ManyToMany relationship

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):-

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.

@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:

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)

like image 465
nspessot Avatar asked May 14 '14 03:05

nspessot


People also ask

How do you resolve a detached entity passed to persist?

The solution is simple, just use the CascadeType. MERGE instead of CascadeType. PERSIST or CascadeType. ALL .

What is detached entity passed to persist?

A detached entity (a.k.a. a detached object) is an object that has the same ID as an entity in the persistence store but that is no longer part of a persistence context (the scope of an EntityManager session).

What is JPA orphanRemoval?

orphanRemoval is an entirely ORM-specific thing. It marks "child" entity to be removed when it's no longer referenced from the "parent" entity, e.g. when you remove the child entity from the corresponding collection of the parent entity.

What is a detached entity?

A detached entity is just an ordinary entity POJO whose identity value corresponds to a database row. The difference from a managed entity is that it's not tracked anymore by any persistence context. An entity can become detached when the Session used to load it was closed, or when we call Session.

What is entityexistsexception in JPA persistence?

The contract for persist (see section 3.2.1 of the JPA 1.0 spec) explicitly states that an EntityExistsException is thrown by the persist method when the object passed in is a detached entity. Or any other PersistenceException when the persistence context is flushed or the transaction is committed.

Why is my child entity being detached from Hibernate?

The child entity was immediately becoming detached because there was no active Hibernate Session context. Providing a Spring (Data JPA) transaction ensures a Hibernate Session is present.

What is @ID annotation in hibernate?

ps: the @Id annotation is the one that hibernate uses to identify the access type. The exception is : detached entity passed to persist Why improving consistency makes that it works? Ok, consistency was repaired but object is still detached. @Sam, thanks a lot for your explanation. But, I don't understand still.

What is a many to many relationship in JPA?

In a JPA many to many relationship, if cascade type has been set at CascadeType.PERSIST (or CascadeType.ALL, which includes CascadeType.PERSIST), then while saving the parent and updating it with references of the child, it will try to save the child again.


2 Answers

I had the same problem and solved it by removing the cascade = CascadeType.PERSIST.

In your case you use CascadeType.ALL, which is equivalent to also using the PERSIST, according to the documentation:

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}.

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.

like image 140
Renato Borges Avatar answered Sep 20 '22 09:09

Renato Borges


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

@GeneratedValue(strategy=GenerationType.AUTO) 

But you got products from repository and ids are not null.

There 2 options to resolve your issue:

  1. remove (cascade = CascadeType.ALL) on products of Reservation
  2. or remove @GeneratedValue(strategy=GenerationType.AUTO) on id of Product
like image 39
My Pham Avatar answered Sep 22 '22 09:09

My Pham