복붙노트

[SPRING] Spring 데이터 jpa와 최대 절전 모드로 분리 된 엔티티가 ManyToMany 관계에서 지속되도록 전달됨

SPRING

Spring 데이터 jpa와 최대 절전 모드로 분리 된 엔티티가 ManyToMany 관계에서 지속되도록 전달됨

이미 유지 된 다른 객체와 수 많은 관계가있는 객체를 유지하려고합니다.

여기에 내 영속 객체가 있습니다 (그들은 이미 db로 유지되어 있습니다, 이것은 mysql입니다) : 생성물

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

다음은 전체 스택 추적입니다.

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)

해결법

  1. ==============================

    1.나는 같은 문제를 가지고 계단식 = CascadeType.PERSIST를 제거하여 해결했다.

    나는 같은 문제를 가지고 계단식 = CascadeType.PERSIST를 제거하여 해결했다.

    귀하의 경우에는 문서에 따라 PERSIST를 사용하는 것과 동일한 CascadeType.ALL을 사용하십시오.

    이는 예약 DAO.save (예약)에 예약을 저장하려고하면 연결된 Product 객체를 유지하려고 시도한다는 것을 의미합니다. 그러나이 객체는이 세션에 첨부되지 않습니다. 그래서 오류가 발생합니다.

  2. ==============================

    2.예외는 예약을 저장할 때 관련 제품을 지속 시키려고 최대 절전 모드로 제공됩니다. 제품의 ID가 @GeneratedValue (strategy = GenerationType.AUTO)로 주석되어 있기 때문에 ID가없는 경우에만 제품을 유지할 수 있습니다.

    예외는 예약을 저장할 때 관련 제품을 지속 시키려고 최대 절전 모드로 제공됩니다. 제품의 ID가 @GeneratedValue (strategy = GenerationType.AUTO)로 주석되어 있기 때문에 ID가없는 경우에만 제품을 유지할 수 있습니다.

    하지만 저장소에서 제품을 가져 왔고 ID는 null이 아닙니다.

    문제를 해결할 수있는 2 가지 옵션이 있습니다.

  3. ==============================

    3.관계의 양면이 코드에서 올바르게 유지되도록해야합니다.

    관계의 양면이 코드에서 올바르게 유지되도록해야합니다.

    아래와 같이 예약을 업데이트 한 다음 해당 메소드를 Product에 추가하십시오.

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

    이제 Product 또는 Reservation에서 save를 호출 할 수 있어야하며 모든 것이 예상대로 작동해야합니다.

  4. ==============================

    4.귀하의 특수 효과가 약간 잘못되었다는 느낌이 들었습니다. 많은 예제가 아니라 예제 7.24를보고 주석과 일치하는지 확인하십시오. 세트를 사용하는 데 문제가 없어야하므로 Collection 데이터 유형을 무시하십시오. 제품 카탈로그에 Cascade = CascadeType.ALL이 누락되어 있음을 확인했지만 문제가있는 경우 해결할 수 없습니다.

    귀하의 특수 효과가 약간 잘못되었다는 느낌이 들었습니다. 많은 예제가 아니라 예제 7.24를보고 주석과 일치하는지 확인하십시오. 세트를 사용하는 데 문제가 없어야하므로 Collection 데이터 유형을 무시하십시오. 제품 카탈로그에 Cascade = CascadeType.ALL이 누락되어 있음을 확인했지만 문제가있는 경우 해결할 수 없습니다.

    실제 예외가 말하는 것은 제품의 컬렉션을 저장할 때 Product 객체가 실제로 저장되지 않았다는 것입니다. 그래서 내가 주석에 문제가 있다고 생각합니다.

    그것을 밖으로 시도하고 어디서나하면 알려주세요.

  5. ==============================

    5.entityManager.merge ()가 좋은 옵션입니다. 분리 된 객체를 세션에서 병합합니다. 캐스케이드 유형을 변경할 필요가 없습니다.

    entityManager.merge ()가 좋은 옵션입니다. 분리 된 객체를 세션에서 병합합니다. 캐스케이드 유형을 변경할 필요가 없습니다.

  6. from https://stackoverflow.com/questions/23645091/spring-data-jpa-and-hibernate-detached-entity-passed-to-persist-on-manytomany-re by cc-by-sa and MIT license