prosource

Spring 데이터 JPA 및 최대 절전 모드 분리 엔티티가 Multi-to-Many 관계를 유지하도록 전달되었습니다.

probook 2023. 8. 11. 22:22
반응형

Spring 데이터 JPA 및 최대 절전 모드 분리 엔티티가 Multi-to-Many 관계를 유지하도록 전달되었습니다.

저는 이미 지속된 다른 개체와 다대다 관계가 있는 개체를 유지하려고 합니다.

다음은 나의 지속적인 객체입니다(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;
    }
}

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

심각: 경로 [/web]에서 servlet [dispatcher]에 대한 servlet.service()가 예외 [요청 처리 실패, 중첩 예외는 org.springframework.dao입니다.잘못된 DataAccessApiUsageException: 분리된 엔티티가 persist: com.reservation에 전달되었습니다.service.domain.제품. 중첩 예외는 org.hibernate입니다.persistentObjectException: 분리된 엔티티가 persist: com.reservation으로 전달되었습니다.service.domain.제품]과(와) 근본 원인 org.hibernate.persistentObjectException: 분리된 엔티티가 persist: com.reservation으로 전달되었습니다.service.domain.org.hibernate.event.internal에 있는 제품입니다.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:141)

저도 같은 문제를 가지고 있었고 그것을 제거함으로써 해결했습니다.cascade = CascadeType.PERSIST.

사용하는 경우CascadeType.ALL이는 설명서에 따라 PERST를 사용하는 것과 동일합니다.

연결된 엔티티로 전파되는 계단식 작업 집합을 정의합니다.값 캐스케이드=ALL은 캐스케이드={와 같습니다.지속, 병합, 제거, 새로 고침, 분리}.

이는 예약을 저장하려고 할 때를 의미합니다.reservationDAO.save(reservation)또한 연결된 제품 개체를 유지하려고 시도합니다.그러나 이 개체는 이 세션에 연결되어 있지 않습니다.그래서 오류가 발생합니다.

예약을 저장할 때 최대 절전 모드가 관련 제품을 유지하려고 할 때 예외가 발생합니다.제품의 ID에 주석이 달렸기 때문에 ID가 없는 경우에만 제품을 유지할 수 있습니다.

@GeneratedValue(strategy=GenerationType.AUTO)

그러나 저장소에서 제품을 가져왔으며 해당 ID는 null이 아닙니다.

문제를 해결하기 위한 두 가지 옵션이 있습니다.

  1. 거한제를 합니다.(cascade = CascadeType.ALL).
  2. 는또제를 제거합니다.@GeneratedValue(strategy=GenerationType.AUTO) ID 에 ID 라따의 에 있습니다.

코드에서 관계의 양쪽이 제대로 유지되는지 확인해야 합니다.

아래와 같이 예약을 업데이트한 후 해당 메소드를 제품에 추가합니다.

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

이제 제품 또는 예약에 대한 저장을 호출할 수 있으며 모든 것이 예상대로 작동하므로 행복할 것입니다.

@ 캐스케이드 유형을 제거합니다.모두 @ManytoMany 관계에서, 이것은 저에게 효과가 있었습니다.

저도 비슷한 문제가 있었어요.교체했습니다CascadeType.ALL와 함께CascadeType.MERGE그리고 그것은 저에게 효과가 있었습니다.

매핑은 다음과 같을 수 있습니다.ManyToMany또는OneToOne.

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

저는 당신의 주석이 약간 틀렸다고 생각합니다.별로 그렇지는 않지만, 여기 예제 7.24를 보고 주석과 일치하는지 확인하십시오.무시합니다.Collection하지만 데이터 유형, 사용하는 데 문제가 없어야 하기 때문입니다.Set저는 당신이 a를 놓치고 있다는 것을 알아챘습니다.cascade=CascadeType.ALL의 신의에Product하지만 그게 문제인지 아닌지는 알 수가 없습니다.

은 당신의 입니다.Product개체가컬렉 저할고려저않장았때의 할 때 .Product주석에 그래서 당신의 주석에 문제가 있다고 생각합니다.

시험해 보고 어디든 있으면 알려주세요.

저장 및 병합을 사용하여 업데이트해야 하지만 먼저 ID가 null인지 확인해야 합니다.

@Override
public Registration save(Registration registration) {
    if(registration.getId() == null) {
        _entityManager.persist(registration);
    }else {
        _entityManager.merge(registration);
    }
    return registration;
}

언급URL : https://stackoverflow.com/questions/23645091/spring-data-jpa-and-hibernate-detached-entity-passed-to-persist-on-manytomany-re

반응형