传递Spring Data JPA和Hibernate分离实体以保持ManyToMany关系

kr98yfug  于 2022-12-13  发布在  Spring
关注(0)|答案(7)|浏览(186)

我正在尝试持久化一个与其他已持久化对象具有多对多关系的对象。
下面是我的持久化对象(它们已经持久化在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类,它接收一个产品名称数组,使用该名称查找产品并将它们放入reservation对象中。

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

这是我的Spring配置文件:

@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。无效数据访问应用程序使用异常错误:传递到持久化的分离实体:网站.预订.服务.域名.产品;嵌套的异常是组织。传递到持久化的分离实体:出现根原因为org. hib的持久对象异常:传递到持久化的分离实体:如果您有任何问题,请联系我们。如果您有问题,请联系我们。

0wi1tuuw

0wi1tuuw1#

我遇到了同样的问题,通过删除X1 M0 N1 X解决了它。
在本例中,您使用CascadeType.ALL,根据文档,这相当于也使用PERSIST:
定义传播到关联实体的可级联操作集。值cascade=ALL等效于cascade={PERSIST,MERGE,REMOVE,REFRESH,DETACH}。
这意味着,当您尝试在reservationDAO.save(reservation)上保存预订时,它还将尝试保留关联的产品对象。但此对象未附加到此会话。因此出现错误。

eeq64g8w

eeq64g8w2#

当保存预订时,Hibernate尝试持久化关联的产品时会出现异常。

@GeneratedValue(strategy=GenerationType.AUTO)

但您从存储库中获取了产品,并且ID不为空。
有两个选项可解决您的问题:
1.删除保留的产品上的(cascade = CascadeType.ALL)
1.或删除产品ID上的@GeneratedValue(strategy=GenerationType.AUTO)

tyu7yeag

tyu7yeag3#

您需要确保在代码中正确维护关系的双方。
如下所示更新Reservation,然后将相应的方法添加到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);
    }
}

现在你应该能够调用保存产品或预订和一切都应该工作的预期,所以你会很高兴。

8tntrjer

8tntrjer4#

移除@ManytoMany关系中的@ CascadeType.ALL,这对我很有效。

9jyewag0

9jyewag05#

entityManager.merge()是一个很好的选项。它将合并会话中分离的对象。不需要更改任何cascadeType。

bis0qfac

bis0qfac6#

我感觉您的注解有一点不正确。虽然不太正确,但请查看示例7.24,看看它是否与您的注解匹配。忽略Collection数据类型,因为使用Set应该不会有任何问题。我注意到您的Product集合中缺少了cascade=CascadeType.ALL,但我不知道这是不是问题所在。
实际的异常是说,当它试图保存Product的集合时,你的Product对象实际上还没有被保存。这就是为什么我认为这是你的注解的问题。
试试看,如果你有什么进展就告诉我。

puruo6ea

puruo6ea7#

我也遇到过类似的问题。我用CascadeType.MERGE替换了CascadeType.ALL,它对我很有效。
Map可以是ManyToManyOneToOne

相关问题