java 休眠问题:@OneToMany 注释返回重复项
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13256569/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Hibernate issue: @OneToMany annotation returns duplicates
提问by pitschr
I am facing a problem with Hibernate (4.3.0) where as unidirectional @OneToMany returns duplicates.
我正面临 Hibernate (4.3.0) 的问题,因为单向 @OneToMany 返回重复项。
My database structure (MySQL with InnoDB) where as 'entry' table has a 1:N relationship with 'entry_address' table. The 'entry' table is the main table and 'entry_address' is a sub-table of 'entry' table.
我的数据库结构(带有 InnoDB 的 MySQL),其中 'entry' 表与 'entry_address' 表具有 1:N 关系。“entry”表是主表,“entry_address”是“entry”表的子表。
CREATE TABLE IF NOT EXISTS `entry` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(500) NOT NULL,
`active` int(1) NOT NULL DEFAULT '0',
`modifiedTS` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
`createdTS` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;
INSERT INTO `entry` (`id`, `name`, `active`, `modifiedTS`, `createdTS`) VALUES
(1, 'Test1', 0, '2012-11-05 13:41:03', '2012-11-01 10:11:22'),
(2, 'Test2', 1, '2012-11-05 11:19:37', '2012-11-01 10:11:33'),
(3, 'Test3', 1, '2012-11-05 11:19:37', '2012-11-01 10:11:44');
CREATE TABLE IF NOT EXISTS `entry_address` (
`id` int(10) unsigned NOT NULL,
`precedence` int(1) NOT NULL DEFAULT '0',
`line` varchar(255) DEFAULT NULL,
`line2` varchar(255) DEFAULT NULL,
`street` varchar(255) DEFAULT NULL,
`street2` varchar(255) DEFAULT NULL,
`zip` int(5) DEFAULT NULL,
`city` varchar(255) DEFAULT NULL,
`country` varchar(255) DEFAULT NULL,
UNIQUE KEY `entry_address_uq` (`id`,`precedence`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `entry_address` (`id`, `precedence`, `line`, `line2`, `street`, `street2`, `zip`, `city`, `country`) VALUES
(1, 0, 'Line4.1', 'Line4.2', 'Street4.1', 'Street4.2', 9488, 'Schellenberg', 'Liechtenstein'),
(2, 10, 'Line1.1', 'Line1.2', 'Street1.1', 'Street1.2', 9492, 'Eschen', 'Liechtenstein'),
(2, 20, 'Line2.1', 'Line2.2', 'Street2.1', 'Street2.2', 9490, 'Vaduz', 'Liechtenstein'),
(2, 30, 'Line3.1', 'Line3.2', 'Street3.1', 'Street3.2', 9494, 'Schaan', 'Liechtenstein'),
(3, 10, 'Line5.1', 'Line5.2', 'Street5.1', 'Street5.2', 9492, 'Eschen', 'Liechtenstein'),
(3, 20, 'Line6.1', 'Line6.2', 'Street6.1', 'Street6.2', 9492, 'Eschen', 'Liechtenstein');
ALTER TABLE `entry_address`
ADD CONSTRAINT `entry_address_fk` FOREIGN KEY (`id`) REFERENCES `entry` (`id`);
Here's the minimal code of "entry" entity.
这是“入口”实体的最小代码。
import java.util.Collection;
import javax.persistence.*;
@Entity
@Table(name = "entry")
public class Entry {
@Id
@GeneratedValue
@Column(name = "id")
private Integer id;
@Column(name = "name")
private String name;
@OneToMany(fetch=FetchType.EAGER)
@JoinColumn(name = "id")
private Collection<EntryAddress> addresses;
@Override
public String toString() {
return String.format("Entry [id=%s, name=%s, addresses=%s]", id, name, addresses);
}
}
Here's the minimal code of "entry_address" entity:
这是“entry_address”实体的最小代码:
import javax.persistence.*;
@Entity
@Table(name = "entry_address")
public class EntryAddress {
@Id
@Column(name = "id")
private Integer id;
@Column(name = "line")
private String line;
@Override
public String toString() {
return String.format("EntryAddress [line=%s]", line);
}
}
This is the query done by Hibernate (looks good!): Hibernate: select this_.id as id0_1_, this_.name as name0_1_, addresses2_.id as id0_3_, addresses2_.id as id1_3_, addresses2_.id as id1_0_, addresses2_.line as line1_0_ from entry this_ left outer join entry_address addresses2_ on this_.id=addresses2_.id
这是 Hibernate 完成的查询(看起来不错!): Hibernate:选择 this_.id 作为 id0_1_,this_.name 作为 name0_1_,addresses2_.id 作为 id0_3_,addresses2_.id 作为 id1_3_,addresses2_.id 作为 id1_0_,addresses2_.line 作为line1_0_ from entry this_ 左外连接 entry_addressaddresses2_ on this_.id=addresses2_.id
But if I run JUnit using:
但是,如果我使用以下命令运行 JUnit:
import java.util.Collection;
import junit.framework.Assert;
import li.pitschmann.transaction.dao.EntryDao;
import li.pitschmann.transaction.entity.Entry;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
@ContextConfiguration(locations={"file:**/web-spring.xml"})
public class EntryDaoTest extends AbstractTransactionalJUnit4SpringContextTests {
@Autowired
private EntryDao entryDao;
@Test
public void findAllEntries() {
Collection<Entry> entries = entryDao.findEntries();
Assert.assertNotNull(entries);
for (Entry e : entries) {
System.out.println("++: " + e);
}
// Assert.assertEquals(3, entries.size());
}
}
import java.util.Collection;
import org.hibernate.SessionFactory;
import org.springframework.transaction.annotation.Transactional;
import li.pitschmann.transaction.dao.EntryDao;
import li.pitschmann.transaction.entity.Entry;
public class EntryDaoImpl implements EntryDao {
private SessionFactory sessionFactory;
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Transactional
public Collection<Entry> findEntries() {
return sessionFactory.getCurrentSession().createCriteria(Entry.class).list();
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
Spring XML (most important part, Spring 3.1.2.RELEASE):
Spring XML(最重要的部分,Spring 3.1.2.RELEASE):
<tx:annotation-driven transaction-manager="transactionManager" />
<context:annotation-config />
<!-- MySQL DataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/entry_db" />
<property name="user" value="root" />
<property name="password" value="" />
</bean>
<!-- Session Factory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>li.pitschmann.transaction.entity</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
The console log is:
控制台日志是:
++: Entry [id=1, name=Test1, addresses=[EntryAddress [line=Line4.1]]]
++: Entry [id=2, name=Test2, addresses=[EntryAddress [line=Line1.1], EntryAddress [line=Line1.1], EntryAddress [line=Line1.1]]]
++: Entry [id=2, name=Test2, addresses=[EntryAddress [line=Line1.1], EntryAddress [line=Line1.1], EntryAddress [line=Line1.1]]]
++: Entry [id=2, name=Test2, addresses=[EntryAddress [line=Line1.1], EntryAddress [line=Line1.1], EntryAddress [line=Line1.1]]]
++: Entry [id=3, name=Test3, addresses=[EntryAddress [line=Line5.1], EntryAddress [line=Line5.1]]]
++: Entry [id=3, name=Test3, addresses=[EntryAddress [line=Line5.1], EntryAddress [line=Line5.1]]]
I also tried to use @OneToMany(fetch=FetchType.LAZY) instead of FetchType.EAGER - same issue with duplicate addresses.
我还尝试使用 @OneToMany(fetch=FetchType.LAZY) 而不是 FetchType.EAGER - 重复地址的相同问题。
++: Entry [id=1, name=Test1, addresses=[EntryAddress [line=Line4.1]]]
++: Entry [id=2, name=Test2, addresses=[EntryAddress [line=Line1.1], EntryAddress [line=Line1.1], EntryAddress [line=Line1.1]]]
++: Entry [id=3, name=Test3, addresses=[EntryAddress [line=Line5.1], EntryAddress [line=Line5.1]]]
Expectation
期待
This is my expecation (3 entry objects with different addresses):
这是我的期望(具有不同地址的 3 个条目对象):
++: Entry [id=1, name=Test1, addresses=[EntryAddress [line=Line4.1]]]
++: Entry [id=2, name=Test2, addresses=[EntryAddress [line=Line1.1], EntryAddress [line=Line2.1], EntryAddress [line=Line3.1]]]
++: Entry [id=3, name=Test3, addresses=[EntryAddress [line=Line5.1], EntryAddress [line=Line6.1]]]
Is there a bug in Hibernate or am I doing something wrong? Hope someone can help me to find the root cause?! Thank you :-)
Hibernate 中是否存在错误或我做错了什么?希望有人能帮我找到根本原因?!谢谢 :-)
采纳答案by Dave L.
You might need to modify your method like so:
您可能需要像这样修改您的方法:
@SuppressWarnings("unchecked")
@Transactional
public Collection<Entry> findEntries() {
return sessionFactory.getCurrentSession()
.createCriteria(Entry.class)
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.list();
}
Also, change addresses
to a Set
:
另外,更改addresses
为Set
:
@OneToMany(fetch=FetchType.EAGER)
@JoinColumn(name = "id")
private Set<EntryAddress> addresses;
Edit:
编辑:
Oh...In EntryAddress
you have id
defined as the @Id
but it is not unique. You should make id
the primary key and have it auto increment like you do in Entry
. Then create another field in EntryAddress
that is the foreign key to Entry
called something like entry_id
.
哦...在EntryAddress
你已经id
定义为@Id
但它不是唯一的。您应该制作id
主键并让它像在Entry
. 然后在EntryAddress
其中创建另一个字段,该字段是Entry
调用类似entry_id
.