使用一对多映射保存对象时出现 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="&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 问题的主要内容,如果未能解决你的问题,请参考以下文章