使用一对多映射保存对象时出现 Spring + Hibernate id 问题

Posted

技术标签:

【中文标题】使用一对多映射保存对象时出现 Spring + Hibernate id 问题【英文标题】:Spring + Hibernate id issue while saving object with one-to-many mapping 【发布时间】:2013-05-06 16:31:37 【问题描述】:

我在使用 Hibernate 4 和 Spring 3 时遇到问题。我也在使用 BoneCP。我正在尝试保存包含许多图像的文章,并且效果很好。但是,当我尝试获取任何图像的 ID 时,它返回 0。但是,在数据库中,一切都保存得很好。我猜问题出在关系持续存在或我对 Hibernate 的理解中,因为执行后 id 是有效的,但我找不到它。

我的文件: spring-hibernate.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">    

    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" autowire="autodetect">
        <property name="dataSource" ref="dataSource" />
        <property name="annotatedClasses">
            <list>
<value>com.smiechmateusz.model.Article</value>
<value>com.smiechmateusz.model.Image</value>
            </list>
        </property>

        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.mysql5Dialect</prop>
            </props>
        </property>
    </bean>  

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <property name="targetDataSource">
            <ref local="mainDataSource" />
        </property>
    </bean>

    <bean id="mainDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/java" />
        <property name="username" value="root"/>
        <property name="password" value="toor"/>
        <property name="idleConnectionTestPeriod" value="60"/>
        <property name="idleMaxAge" value="240"/>    
        <property name="maxConnectionsPerPartition" value="60"/>
        <property name="minConnectionsPerPartition" value="20"/>
        <property name="partitionCount" value="3"/>
        <property name="acquireIncrement" value="10"/>                           
        <property name="statementsCacheSize" value="50"/>
        <property name="releaseHelperThreads" value="3"/>
    </bean>

    <bean id="txManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    <tx:annotation-driven transaction-manager="txManager" />

    <context:annotation-config />
    <context:component-scan base-package="com.smiechmateusz.dao" />

     <bean id="hibernateConfiguration" factory-bean="&amp;sessionFactory" factory-method="getConfiguration" /> 

    <bean id="AbstractHibernateDAO" abstract="true"
        class="com.smiechmateusz.dao.AbstractDAO">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    <bean id="ImageDAO" parent="AbstractHibernateDAO" class="com.smiechmateusz.dao.ImageDAO" />
    <bean id="ArticleDAO" parent="AbstractHibernateDAO" class="com.smiechmateusz.dao.ArticleDAO" />
</beans>

AbstractDAO.java

package com.smiechmateusz.dao;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.PersistentObjectException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
@Transactional()
public abstract class AbstractDAO<T extends Serializable> 

    private final Class<T> clazz;
    @Autowired
    SessionFactory sessionFactory;

    public AbstractDAO(final Class< T> clazzToSet) 
    
        this.clazz = clazzToSet;
    

    public T getById(final Long id) 
    
        if (id != null)
            return (T) this.getCurrentSession().get(this.clazz, id);
        return null;
    

    public List<T> getAll() 
    
        return this.getCurrentSession().createQuery("from " + this.clazz.getName()).list();
    

    public void create(final T entity) 
    
        if (entity != null)
        
            try
            
                this.getCurrentSession().persist(entity);
            
            catch (PersistentObjectException e)
            
                this.getCurrentSession().saveOrUpdate(entity);
            
        
    

    public void update(final T entity) 
    
        if (entity != null)
        
            this.getCurrentSession().merge(entity);

        
    

    public void delete(final T entity) 
    
        if (entity != null)
            this.getCurrentSession().delete(entity);
    

    public void deleteById(final Long entityId) 
    
        final T entity = this.getById(entityId);
        if (entity != null)
            this.delete(entity);
    

    public void setSessionFactory(SessionFactory sessionFactory) 
    
        this.sessionFactory = sessionFactory;
    

    protected final Session getCurrentSession() 
    
        return this.sessionFactory.getCurrentSession();
    

    public List<T> getWithCriteria(List<Criterion> criteria)
    
        Criteria c = this.getCurrentSession().createCriteria(this.clazz);
        for (Criterion cr : criteria)
        
            c.add(cr);
        
        return c.list();
    

ImageDAO.java

package com.smiechmateusz.dao;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.smiechmateusz.model.Image;

@Service
@Transactional()
public class ImageDAO extends AbstractDAO

    public ImageDAO()
    
        super(Image.class);
    

ArticleDAO.java

package com.smiechmateusz.dao;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.smiechmateusz.model.Article;

@Service
@Transactional()
public class ArticleDAO extends AbstractDAO

    public ArticleDAO()
    
        super(Article.class);
    

Image.java

package com.smiechmateusz.model;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="Image")
public class Image implements Serializable

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="id")
    long id;
    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
    @JoinColumn(name="article")
    Article article;
    @Column(name="path")
    String path;
    @Column(name="type")
    int type;
    public long getId()
    
        return id;
    
    public void setId(long id)
    
        this.id = id;
    
    public Article getArticle()
    
        return article;
    
    public void setArticle(Article article)
    
        this.article = article;
    
    public String getPath()
    
        return path;
    
    public void setPath(String path)
    
        this.path = path;
    
    public int getType()
    
        return type;
    
    public void setType(int type)
    
        this.type = type;
    

Article.java

package com.smiechmateusz.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name="Article")
public class Article implements Serializable

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="id")
    long id;
    @Column(name="images")
    @OneToMany(targetEntity=com.smiechmateusz.model.Image.class, mappedBy="article",cascade=CascadeType.ALL, fetch=FetchType.LAZY)
    List<Image> images;
    @Column(name="description")
    String description;
    @Temporal(TemporalType.DATE)
    @Column(name="addDate")
    Date addDate;
    public Article()
    
        this.images = new ArrayList<Image>();
    
    public long getId()
    
        return id;
    
    public void setId(long id)
    
        this.id = id;
    
    public List<Image> getImages()
    
        return images;
    
    public void setImages(List<Image> images)
    
        this.images = images;
    
    public String getDescription()
    
        return description;
    
    public void setDescription(String description)
    
        this.description = description;
    
    public Date getAddDate()
    
        return addDate;
    
    public void setAddDate(Date addDate)
    
        this.addDate = addDate;
    

我正在使用以下代码进行测试:

ApplicationContext context = new ClassPathXmlApplicationContext("spring-hibernate.xml");
        Configuration config = (Configuration) context.getBean("hibernateConfiguration");
        new SchemaExport(config).create(true, true);
        ArticleDAO ad = (ArticleDAO) context.getBean("ArticleDAO");
        ImageDAO id = (ImageDAO) context.getBean("ImageDAO");
        Article a = new Article();
        Image i = new Image();
        i.setPath("path");
        i.setType(0);
        i.setArticle(a);
        a.setAddDate(new Date());
        a.setDescription("desc");
        Image i2 = new Image();
        i2.setPath("path2");
        i2.setType(1);
        i2.setArticle(a);
        List<Image> list = new ArrayList<Image>();
        list.add(i);
        list.add(i2);
        ad.create(a);
        a.setImages(list);
        ad.update(a);
        System.out.println(i.getId()); //Returns 0 instead of 1.

数据库查询:

mysql> SELECT * FROM Image;
+----+-------+------+---------+
| id | path  | type | article |
+----+-------+------+---------+
|  1 | path  |    0 |       1 |
|  2 | path2 |    1 |       1 |
+----+-------+------+---------+
2 rows in set (0.00 sec)

编辑:

如果我尝试获取文章 a 的 ID,它会返回有效的 id (1)。

System.out.println(a.getId());
1

【问题讨论】:

返回文章的id吗? 【参考方案1】:

问题出在AbstractDAO.update() 方法中。您正在尝试 merge 数据库中的所有内容,但您应该 save() 新实体。

看看这个问题Hibernate : Downside of merge() over update()

【讨论】:

以上是关于使用一对多映射保存对象时出现 Spring + Hibernate id 问题的主要内容,如果未能解决你的问题,请参考以下文章

在dapper中使用一对多关系时出现splitOn错误

序列化对象时出现循环引用错误。一对多关联对象

尝试在映射服务器上将 XLSM 保存为 CSV 时出现“对象 _workbook 的方法保存失败”错误

hibernate一对多单向关联时更新问题

hibernate多对多映射中间表有多余字段问题该如何映射

一对多映射不起作用Spring数据JPA