2014-05-14 27 views
13

Ich versuche, ein Objekt persistent zu machen, das eine manytomany-Beziehung mit anderen bereits bestehenden Objekten hat.Federdaten jpa und Hibernate losgelöste Entität weitergegeben, um auf ManyToMany-Beziehung persistent zu bleiben

Hier ist mein persistentes Objekt (sie sind bereits in der db beharren, die eine mysql): Produkt

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

Hier ist mein kein persistentes Objekt, das ich versuche,

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

Dies ist meine ReservationService-Klasse, die ein Array von Produktnamen empfängt, die Produkte anhand des Namens anschaut und sie in das Reservierungsobjekt einfügt.

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

Hier ist meine ProductDAO Schnittstelle:

public interface ProductDAO extends JpaRepository<Product, Integer> { 

    public Product findByProductName(String productName); 
} 

Diese meine Feder-Konfigurationsdatei ist:

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

Hier ist die vollständige 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) 

Antwort

0

Ich habe ein Gefühl, dass deine Anmerkungen leicht sind falsch. Aber nicht viel, werfen Sie einen Blick auf Example 7.24 here und sehen Sie, ob es Ihren Anmerkungen entspricht. Ignorieren Sie jedoch den Collection Datentyp, da Sie keine Probleme mit einem Set haben sollten. Ich merke, dass Sie eine cascade=CascadeType.ALL auf Ihrer Sammlung vermissen, aber ich kann nicht herausfinden, ob das das Problem ist oder nicht.

Was die eigentliche Ausnahme ist, ist, dass Ihre Objekt (e) nicht tatsächlich gespeichert wurden, wenn es versucht, die Sammlung von Product 's zu speichern. Deshalb denke ich, dass es ein Problem mit deinen Anmerkungen ist.

Probieren Sie es aus und lassen Sie mich wissen, wenn Sie irgendwo ankommen.

0

Sie müssen sicherstellen, dass beide Seiten der Beziehung in Ihrem Code ordnungsgemäß verwaltet werden.

Aktualisieren Sie die Reservierung wie folgt und fügen Sie dann die entsprechenden Methoden zum Produkt hinzu.

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

Und in Produkte:

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

Jetzt sollten Sie in der Lage sein, entweder speichern rufen Produkt oder Reservierung und alles sollte wie erwartet funktionieren.

24

Ich hatte das gleiche Problem und löste es durch Entfernen der cascade = CascadeType.PERSIST.

In Ihrem Fall verwenden Sie CascadeType.ALL, was äquivalent ist auch die PERSIST verwendet, gemäß der Dokumentation:

Definiert den Satz von kaskadierbar Operationen, die an die zugehörige Einheit weitergegeben werden. Der Wert cascade = ALL ist äquivalent zu cascade = {PERSIST, MERGE, REMOVE, REFRESH, DETACH}.

Das bedeutet, wenn Sie versuchen, die Reservierung unter reservationDAO.save(reservation) zu speichern, wird auch versucht, das zugehörige Produktobjekt beizubehalten. Aber dieses Objekt ist dieser Sitzung nicht zugeordnet. Also der Fehler auftritt.

+1

Ich habe das gleiche Problem, aber nachdem ich CascadeType entfernen, gibt es mir 'MySQLNonTransientConnectionException: Keine Operationen erlaubt nach Anweisung geschlossen.', eine Idee? – Newbie

0

entityManager.merge() ist eine gute Option. Es wird die gelösten Objekte in der Sitzung zusammenführen. Sie müssen keinen cascadeType ändern.

1

Die Ausnahme kommt als Ruhezustand, der versucht, verknüpfte Produkte beizubehalten, wenn Sie die Reservierung speichern. Das Fortbestehen der Produkte ist nur dann erfolgreich, wenn sie keine ID haben, da die ID des Produkts mit @GeneratedValue (strategie = GenerationType.AUTO) versehen ist.

Aber Sie haben Produkte aus dem Repository und IDs sind nicht null.

Es 2 Möglichkeiten um Ihr Problem zu lösen:

  1. remove (Kaskade = CascadeType.ALL) auf Produkte der Reservierung
  2. oder @GeneratedValue (strategy = GenerationType.AUTO) auf id Produkt
  3. entfernen
+0

Das hat mein Problem behoben! – Stugal

Verwandte Themen