spring数据jpa和hibernate分离的实体传递给ManyToMany关系持久化

nsp*_*sot 30 java spring hibernate jpa

我试图坚持一个与已经存在的其他对象有很多关系的对象.

这是我的持久化对象(它们已经存在于db中,这是一个mysql):Product

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我没有持久化的对象,我正在尝试创建它

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的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
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的ProductDAO界面:

public interface ProductDAO extends JpaRepository<Product, Integer> {

    public Product findByProductName(String productName);
}
Run Code Online (Sandbox Code Playgroud)

这是我的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是完整的堆栈跟踪:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

小智 54

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

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

定义传播到关联实体的可级联操作集.值cascade = ALL相当于cascade = {PERSIST,MERGE,REMOVE,REFRESH,DETACH}.

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

  • 我遇到了同样的问题,但是在删除CascadeType之后,它给了我MySQLNonTransientConnectionException:语句关闭后不允许进行任何操作。 (2认同)

小智 26

hibernate试图在保存预订时保留相关产品,这是个例外.只有产品的ID被注释@GeneratedValue(strategy = GenerationType.AUTO),持久化产品才会成功

但是你从存储库得到的产品和ids不是空的.

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

  1. 在Reservation的产品上删除(cascade = CascadeType.ALL)
  2. 或删除Product的id上的@GeneratedValue(strategy = GenerationType.AUTO)


Ala*_*Hay 8

您需要确保在您的代码中正确维护关系的双方。

更新 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在产品中:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您应该能够在 Product 或 Reservation 上调用 save 并且一切都应该按预期工作。

  • 这应该是公认的答案,并且应该得到最高的支持。人们应该始终抵制遵循“将此行放入...或删除此...”等答案的冲动。虽然我不怀疑有禁用级联或自动ID生成的好例子,但我认为远大多数情况——以及登陆这里的人们——是因为真正的问题是他们的代码没有正确管理关系。这个答案可能是大多数来到这里的人所缺少的。 (2认同)

Jam*_*ENL 0

我感觉你的注释有点不正确。不过,差别不大,请查看此处的示例 7.24,看看它是否与您的注释匹配。不过,请忽略Collection数据类型,因为使用Set. cascade=CascadeType.ALL我注意到您的收藏中缺少 a Product,但我无法确定这是否是问题所在。

实际的异常是说,当您的Product对象尝试保存 的集合时,您的对象实际上尚未保存Product。这就是为什么我认为你的注释有问题。

尝试一下,如果你有任何进展请告诉我。