既に永続化されている他のオブジェクトと多対多の関係を持つオブジェクトを永続化しようとしています。
ここに私の永続化されたオブジェクトがあります(これらはMySqlであるdbに既に永続化されています):
製品
@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;
}
}
ここに私が作成しようとしている私の永続化されていないオブジェクトがあります
@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;
}
}
これは私の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
}
}
これが私のProductDAO
インターフェースです:
public interface ProductDAO extends JpaRepository<Product, Integer> {
public Product findByProductName(String productName);
}
これは私の春の設定ファイルです:
@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;
}
}
完全なスタックトレースを次に示します。
重大:パス[/ web]を持つコンテキストのサーブレット[dispatcher]のServlet.service()は例外をスローしました[リクエスト処理に失敗しました。ネストされた例外は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 org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.Java:141)
同じ問題が発生したため、_cascade = CascadeType.PERSIST
_を削除して解決しました。
あなたのケースでは、CascadeType.ALL
_を使用します。これは、ドキュメントによると、PERSISTを使用するのと同等です。
関連するエンティティに伝播される一連のカスケード可能な操作を定義します。値cascade = ALLはcascade = {PERSIST、MERGE、REMOVE、REFRESH、DETACH}と同等です。
つまり、reservationDAO.save(reservation)
で予約を保存しようとすると、関連するProductオブジェクトも永続化しようとします。ただし、このオブジェクトはこのセッションにアタッチされていません。そのため、エラーが発生します。
例外は、予約を保存するときに休止状態が関連製品を保持しようとすることです。製品のIDに注釈が付けられているため、IDがない場合にのみ製品の永続化が成功します。
_@GeneratedValue(strategy=GenerationType.AUTO)
_
ただし、リポジトリから製品を取得したため、IDはnullではありません。
問題を解決するための2つのオプションがあります。
(cascade = CascadeType.ALL)
_を削除します@GeneratedValue(strategy=GenerationType.AUTO)
を削除します関係の両側がコード内で適切に維持されるようにする必要があります。
以下のように予約を更新してから、対応するメソッドを製品に追加します。
@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);
}
}
}
そして製品:
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);
}
}
これで、製品または予約のいずれかでsaveを呼び出すことができ、すべてが期待どおりに機能するはずです。
@ManytoMany関係の@ CascadeType.ALLを削除してください。これは私にとってはうまくいきました。
entityManager.merge()
は良いオプションです。セッション内の切り離されたオブジェクトをマージします。 cascadeTypeを変更する必要はありません。
私はあなたの注釈が少し間違っていると感じています。それほどではありませんが、 例7.24ここ を見て、それが注釈と一致するかどうかを確認してください。ただし、Collection
を使用しても問題はないため、Set
データ型は無視してください。あなたはcascade=CascadeType.ALL
はProduct
コレクションにありますが、それが問題かどうかは判断できません。
実際の例外は、Product
のコレクションを保存しようとしても、Product
オブジェクトが実際に保存されていないということです。それがあなたの注釈に問題があると思う理由です。
それを試してみて、どこに行くか教えてください。