2014-05-14 14 views
13

Sto cercando di mantenere un oggetto che ha molte relazioni con altri oggetti già persistenti.spring data jpa e hibernate entità distaccata passata per persistere su ManyToMany relazione

Ecco il mio oggetto persistente (sono già persistenti nel DB, che è un mysql): prodotto

@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; 
    } 
} 

Ecco il mio nessun oggetto persistente, che sto cercando di creare

@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; 
    } 
} 

Questa è la mia classe ReservationService, che riceve una serie di nomi di prodotti, guarda i prodotti usando il nome e li inserisce nell'oggetto di prenotazione.

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

Ecco la mia interfaccia ProductDAO:

public interface ProductDAO extends JpaRepository<Product, Integer> { 

    public Product findByProductName(String productName); 
} 

Questa è la mia primavera file di configurazione:

@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; 
    } 
} 

Ecco la completa analisi dello stack:

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) 

risposta

0

ho la sensazione che le tue annotazioni siano leggermente errato. Non molto però, dai un'occhiata a Example 7.24 here e verifica se corrisponde alle tue annotazioni. Ignorare il tipo di dati Collection, poiché non si dovrebbero avere problemi nell'utilizzo di Set. Noto che manchi un cascade=CascadeType.ALL nella tua collezione Product, ma non riesco a capire se questo è il problema o no.

L'unica eccezione è che gli oggetti non sono stati effettivamente salvati durante il tentativo di salvare la raccolta di Product. Ecco perché penso che sia un problema con le tue annotazioni.

Provalo e fammi sapere se vai da nessuna parte.

0

È necessario assicurarsi che entrambi i lati della relazione siano gestiti correttamente nel codice.

Aggiorna prenotazione come indicato di seguito, quindi aggiungere i metodi corrispondenti al prodotto.

@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); 
     } 
    } 
} 

E in Prodotti:

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); 
    } 
} 

Ora si dovrebbe essere in grado di chiamare risparmiare su entrambi Prodotto o prenotazione di camere e tutto dovrebbe funzionare come previsto.

24

Ho avuto lo stesso problema e l'ho risolto rimuovendo lo cascade = CascadeType.PERSIST.

Nel tuo caso si utilizza CascadeType.ALL, che equivale anche a utilizzare il persistono, secondo la documentazione:

definisce l'insieme di operazioni in cascata che si propagano per l'entità associata. Il valore cascade = ALL è equivalente a cascade = {PERSIST, MERGE, REMOVE, REFRESH, DETACH}.

significa quando si tenta di salvare la prenotazione on reservationDAO.save(reservation) sarà anche cercare di mantenere l'oggetto prodotto associato. Ma questo oggetto non è allegato a questa sessione. Quindi l'errore si verifica.

+1

Ho avuto lo stesso problema, ma dopo aver rimosso CascadeType, mi dà 'MySQLNonTransientConnectionException: Nessuna operazione consentita dopo l'istruzione chiusa., Qualche idea? – Newbie

0

entityManager.merge() è una buona opzione. Unirà gli oggetti staccati nella sessione. Non c'è bisogno di cambiare alcun tipo di cascata.

1

L'eccezione si presenta come ibernazione nel tentativo di mantenere i prodotti associati quando si salva la prenotazione. Persistere i prodotti è un successo solo se non hanno id perché id di Product è annotato @GeneratedValue (strategy = GenerationType.AUTO)

Ma i prodotti non sono stati cancellati dal repository e gli ID non sono nulli.

Ci

2 opzioni per risolvere il problema:

  1. remove (cascade = CascadeType.ALL) sui prodotti di prenotazione
  2. o rimuovere @GeneratedValue (strategia = GenerationType.AUTO) su id di prodotto
+0

Questo ha risolto il mio problema! – Stugal

Problemi correlati