dljd_(067-070)_hibernate_关联关系映射_自关联
一、自关联
是指,自己即充当一方,又充当多方,是1:n的变型。例如,对于新闻栏目NewsColumn,可以充当一方,即父栏目,也可以充当多方,即子栏目。而这些反映到数据库表中,只有一张表,这张表中具有一个外键,用于表示该栏目的父栏目。一级栏目的外键值为null,而子栏目则具有外键值。还有很多中这种例子,比如企业的员工表。每个员工都有自己的上级(除老板、老板的上级为null)、而自己的上级又在这张员工表中。
二、程序示例(新闻条目类)
新闻条目分为:父条目和子条目。
package edu.aeon.beans; import java.util.HashSet; import java.util.Set; /** * [说明]:新闻条目类 * @author aeon * */ public class NewsLabel { /**新闻条目id*/ private Integer newsId; /**新闻条目标题*/ private String newsTitle; /**新闻条目内容*/ private String newsContext; /**新闻父条目*/ private NewsLabel parentNewsLabel; /**新闻子条目*/ private Set<NewsLabel> childNewsLabels; public NewsLabel() { super(); childNewsLabels=new HashSet<NewsLabel>(); } public NewsLabel(String newsTitle, String newsContext) { this(); this.newsTitle = newsTitle; this.newsContext = newsContext; } public Integer getNewsId() { return newsId; } public void setNewsId(Integer newsId) { this.newsId = newsId; } public String getNewsTitle() { return newsTitle; } public void setNewsTitle(String newsTitle) { this.newsTitle = newsTitle; } public String getNewsContext() { return newsContext; } public void setNewsContext(String newsContext) { this.newsContext = newsContext; } public NewsLabel getParentNewsLabel() { return parentNewsLabel; } public void setParentNewsLabel(NewsLabel parentNewsLabel) { this.parentNewsLabel = parentNewsLabel; } public Set<NewsLabel> getChildNewsLabels() { return childNewsLabels; } public void setChildNewsLabels(Set<NewsLabel> childNewsLabels) { this.childNewsLabels = childNewsLabels; } @Override public String toString() { return "NewsLabel [newId=" + newsId + ", newsTitle=" + newsTitle + ", newsContext=" + newsContext + ", childNewsLabels=" + childNewsLabels + "]"; } }
NewsLabel.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="edu.aeon.beans"> <class name="NewsLabel"> <id name="newsId"> <column name="newsId" sql-type="int(3)"/> <generator class="native"/> </id> <property name="newsTitle"> <column name="newsTitle" sql-type="varchar(200)"/> </property> <property name="newsContext"> <column name="newsContext" sql-type="varchar(1000)"/> </property> <!-- 关联属性的映射配置 --> <!-- name:关联属性的属性名 --> <set name="childNewsLabels" cascade="save-update"> <!-- 关联的外键 --> <key column="pid" /> <!-- 关联到那个类 --> <one-to-many class="NewsLabel" /> </set> <many-to-one name="parentNewsLabel" cascade="save-update" class="NewsLabel" column="pid" /> </class> </hibernate-mapping>
然后将NewsLabel.hbm.xml注册到hibernate的主配置文件里面:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 配置数据库的四要素 --> <property name="hibernate.connection.driver">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/db_test</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <!-- 配置数据库方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property> <!-- 配置数据库连接池 --> <!-- <property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property> --> <!-- 注册当前session上下文 --> <property name="hibernate.current_session_context_class">thread</property> <!-- 自动建表 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 显示sql --> <property name="hibernate.show_sql">true</property> <!-- 格式化sql --> <property name="hibernate.format_sql">true</property> <mapping resource="edu/aeon/beans/NewsLabel.hbm.xml"/> </session-factory> </hibernate-configuration>
单方(父条目)维护时的测试类
package edu.aeon.hibernate.test; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import edu.aeon.aeonutils.hibernate.getsessionutil.GetSessionUtil; import edu.aeon.beans.NewsLabel; /** * [说明]:测试自关联 * @author aeon * */ public class HibernateTest { /** * 自关联 */ @Test public void testOne2Many(){ Session session=null; Transaction transaction=null; try { session = GetSessionUtil.getSession(); System.out.println(session); transaction = session.getTransaction(); transaction.begin(); NewsLabel child1=new NewsLabel("乒乓球", "乒乓球乒乓球乒乓球乒乓球乒乓球乒乓球"); NewsLabel child2=new NewsLabel("篮球", "篮球篮球篮球篮球篮球篮球篮球篮球篮球篮球篮球"); NewsLabel parent=new NewsLabel("体育", "这是体育!"); //有parent去维护 parent.getChildNewsLabels().add(child1); parent.getChildNewsLabels().add(child2); session.save(parent);//由谁维护保存谁 transaction.commit(); } catch (Exception e) { transaction.rollback(); e.printStackTrace(); } } }
测试结果控制台:
Hibernate: create table NewsLabel ( newsId int(3) not null auto_increment, newsTitle varchar(200), newsContext varchar(1000), pid integer, primary key (newsId) ) Hibernate: alter table NewsLabel add constraint FKnxywagk83uy0fknne0t4nf59w foreign key (pid) references NewsLabel (newsId) ThreadLocalSessionContext.TransactionProtectionWrapper[SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] Hibernate: insert into NewsLabel (newsTitle, newsContext, pid) values (?, ?, ?) Hibernate: insert into NewsLabel (newsTitle, newsContext, pid) values (?, ?, ?) Hibernate: insert into NewsLabel (newsTitle, newsContext, pid) values (?, ?, ?) Hibernate: update NewsLabel set pid=? where newsId=? Hibernate: update NewsLabel set pid=? where newsId=?
测试结果截图:
数据库数据信息截图如下:
由多方(子条目)维护关联关系时的测试类:
package edu.aeon.hibernate.test; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import edu.aeon.aeonutils.hibernate.getsessionutil.GetSessionUtil; import edu.aeon.beans.NewsLabel; /** * [说明]:测试自关联 * @author aeon * */ public class HibernateTest { /** * 自关联 */ @Test public void testOne2Many(){ Session session=null; Transaction transaction=null; try { session = GetSessionUtil.getSession(); System.out.println(session); transaction = session.getTransaction(); transaction.begin(); NewsLabel parent=new NewsLabel("体育", "这是体育!"); NewsLabel child=new NewsLabel("乒乓球", "乒乓球乒乓球乒乓球乒乓球乒乓球乒乓球"); //有child去维护 child.setParentNewsLabel(parent); session.save(child);//由谁维护保存谁 transaction.commit(); } catch (Exception e) { transaction.rollback(); e.printStackTrace(); } } }
测试结果:
Hibernate: create table NewsLabel ( newsId int(3) not null auto_increment, newsTitle varchar(200), newsContext varchar(1000), pid integer, primary key (newsId) ) Hibernate: alter table NewsLabel add constraint FKnxywagk83uy0fknne0t4nf59w foreign key (pid) references NewsLabel (newsId) ThreadLocalSessionContext.TransactionProtectionWrapper[SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] Hibernate: insert into NewsLabel (newsTitle, newsContext, pid) values (?, ?, ?) Hibernate: insert into NewsLabel (newsTitle, newsContext, pid) values (?, ?, ?)
测试结果截图:
此时我们去看下数据库中的数据信息:
这样设计的表如何去区分父条目和子条目呢?满足pid is null的条目均为父条目,pid上有主键值指向的是子条目、且该主键值值为它的父条目的id