Problème avec une relation ManytoMany
Bonjour,
J'ai un problème pour faire fonctionner correctement une relation manytomany.
J'utilise Spring en version 4.0.6.RELEASE et Hibernate en version 4.3.5.Final.
Voici le code que je lance :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
@SpringApplicationConfiguration(classes = DomainAndPersistenceConfigTest.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class BookServiceImplTest {
@Autowired
private BookService bookService;
@Test
public void Test2() {
Auteur auteur = new Auteur("Nom1", "Prenom1");
bookService.addBook("Titre1", "ssTitre1", auteur);
bookService.addBook("Titre2", "ssTitre2", auteur);
}
} |
J'ai ce message d'erreur au niveau de la deuxième ligne addbook.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
|
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: mediatheque.entities.Auteur
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 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:289)
at com.sun.proxy.$Proxy41.persist(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:389)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:405)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:390)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:344)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:111)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy48.save(Unknown Source)
at mediatheque.metier.BookServiceImpl.addBook(BookServiceImpl.java:52)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy49.addBook(Unknown Source)
at mediatheque.metier.BookServiceImplTest.Test2(BookServiceImplTest.java:65)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:233)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:176)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: mediatheque.entities.Auteur
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:801)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:794)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:97)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:350)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:293)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:379)
at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:319)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:296)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:118)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:460)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:294)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:194)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149)
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)
... 70 more |
Par contre cela fonctionne correctement, lorsque je fait :
Code:
1 2 3 4 5 6 7
|
Auteur auteur = new Auteur("Nom1", "Prenom1");
bookService.addBook("Titre1", "ssTitre1", auteur);
bookService.addBook("Titre2", "ssTitre2");
bookService.addAuteurBook(2, auteur); |
Extrait de Book.java
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
@Entity
public class Book {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
@Version
protected Long version = (long) 1;
@Column(length = 25)
private String title;
@Column(length = 50, name = "SSTITLE")
private String ssTitle;
@ManyToMany(fetch = FetchType.EAGER, cascade={CascadeType.PERSIST, CascadeType.MERGE} )
@JoinTable(name = "AuteurBook", joinColumns = @JoinColumn(name = "ID_BOOK"), inverseJoinColumns = @JoinColumn(name = "ID_AUTEUR"))
private Collection<Auteur> auteurs = new ArrayList<Auteur>();
public Book() {
}
public Book(String titre, String ssTitre) {
this.title = titre;
this.ssTitle = ssTitre;
}
public Book(String titre, String ssTitre, final Auteur auteur) {
this.title = titre;
this.ssTitle = ssTitre;
this.auteurs.add(auteur);
}
// Getter + setter |
Extrait de Auteur.java
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
@Entity
public class Auteur {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
@Version
protected Long version = (long) 1;
@Column(length = 15)
private String nom;
@Column(length = 15)
private String prenom;
@ManyToMany(fetch = FetchType.EAGER, mappedBy = "auteurs", cascade = {CascadeType.MERGE })
private Collection<Book> books = new ArrayList<Book>();
public Auteur() {
}
public Auteur(String nom, String prenom) {
this.nom = nom;
this.prenom = prenom;
}
public Auteur(Auteur auteur) {
this.nom = auteur.nom;
this.prenom = auteur.prenom;
}
// Getter + setter |
Extrait de BookServiceImpl.java
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
@Service
public class BookServiceImpl implements BookService {
@Resource
private BookRepository bookRepository;
@Transactional
public Book addBook(String titre, String ssTitre, Auteur auteur) {
Book book = new Book(titre, ssTitre);
book.getAuteurs().add(auteur);
return bookRepository.save(book);
}
@Transactional
public void addAuteurBook(Long id, Auteur auteur) {
Book book = bookRepository.findOne(id);
book.getAuteurs().add(auteur);
bookRepository.save(book);
} |
Extrait de BookRepository.java
Code:
1 2 3
|
public interface BookRepository extends CrudRepository<Book, Long> {
} |
Extrait DomainAndPersistenceConfigTest.java
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = { "mediatheque.repositories" })
@Configuration
@ComponentScan(basePackages = { "mediatheque.service" })
public class DomainAndPersistenceConfigTest {
// la source de données H2
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder dataSource = new EmbeddedDatabaseBuilder();
return dataSource.setType(EmbeddedDatabaseType.H2).build();
}
// le provider JPA
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.H2);
return hibernateJpaVendorAdapter;
}
@Bean
public EntityManagerFactory entityManagerFactory(
JpaVendorAdapter jpaVendorAdapter, DataSource dataSource) {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(jpaVendorAdapter);
factory.setPackagesToScan("mediatheque.entities");
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
return factory.getObject();
}
// Transaction manager
@Bean
public PlatformTransactionManager transactionManager(
EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
} |
Pourquoi je ne peux pas initialiser un objet Book avec un objet Auteur déjà existant mais que je peux ajouter ce même objet Auteur à un objet Book déjà crée ?