春季数据JPA和休眠分离实体传递以在多对多关系上持久化

2022-08-31 23:46:51

我正在尝试持久化一个与已经持久化的其他对象具有多对多关系的对象。

这是我的持久化对象(它们已经持久化在数据库,这是一个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] 抛出异常 [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: 分离的实体传递给 persist: com.reservation.service.domain.Product; 嵌套的异常是 org.hibernate.PersistentObjectException: 分离的实体传递给 persist: com.reservation.service.domain.Product] 与根本原因 org.hibernate.PersistentObjectException: detached entitypass to persist: com.reservation.service.domain.Product at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:141)


答案 1

我遇到了同样的问题,并通过删除.cascade = CascadeType.PERSIST

在你的情况下,你使用 ,这相当于也使用 PERSIST,根据文档:CascadeType.ALL

定义传播到关联实体的一组可级联操作。值 cascade=ALL 等效于 cascade={PERSIST、 MERGE、 REMOVE、 REFRESH、 DISULD}。

这意味着当您尝试保存预留时,它也将尝试保留关联的 Product 对象。但此对象未附加到此会话。所以错误发生了。reservationDAO.save(reservation)


答案 2

当您保存预订时,异常是休眠状态,尝试保留关联的产品。只有当产品没有 ID 时,持久化产品才是成功的,因为产品的 ID 被注释了

@GeneratedValue(strategy=GenerationType.AUTO)

但是您从存储库中获取了产品,并且 ID 不为 null。

有 2 个选项可以解决您的问题:

  1. 删除预订产品(cascade = CascadeType.ALL)
  2. 或在产品ID上删除@GeneratedValue(strategy=GenerationType.AUTO)

推荐