单向多对一
首先先了解下数据库中存在的表,以及他们的关系,这里以type表和product_info表为例
type表
product_info表
其中tid是外键,与type表的id字段对应,存在外键约束。
数据库中的表之间多对一是指可以从多的一端找到一的那一端,例如部门和员工的关系是多对一关系,一个部门可以有很多个员工,多个员工从属于一个部门,从员工中的部门号可以得到该员工属于哪个部门,再例如商品是有类型的,惠普电脑和神州电脑这两种商品的类型都是电脑,虽然他们商品信息不同,但类型却都是一样,都是电脑。
在Hibernate中对表之间多对一的关联关系提供了支持,首先开发实体类Type(POJO)它和数据库中type表对应,由于type表中的主键设置了自增,因此,该实体类不用给id赋值。
package com.hibernate.entity;
import java.util.HashSet;
import java.util.Set;
public class Type {
private int id;
private String name;
//set and get
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Type(String name) { //有参构造方法
super();
this.name = name;
}
public Type() { //无参构造方法
super();
}
}
接着开发ProductInfo(POJO)实体类,它与数据库中product_info表对应,其中有关联属性type,对应着product_info表中tid字段(tid是type表的主键)
package com.hibernate.entity;
public class ProductInfo {
//一般属性
private int id;
private String code;
private String name;
private String intro;
//关联属性
private Type type;
//set and get
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIntro() {
return intro;
}
public void setIntro(String intro) {
this.intro = intro;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public ProductInfo() {//无参构造方法
super();
// TODO Auto-generated constructor stub
}
public ProductInfo(String code, String name, String intro, Type type) {//有参构造方法1
super();
this.code = code;
this.name = name;
this.intro = intro;
this.type = type;
}
public ProductInfo(int id, String code, String name, String intro) {//有参构造方法2
super();
this.id = id;
this.code = code;
this.name = name;
this.intro = intro;
}
public ProductInfo(String code, String name, String intro) {//有参构造方法3
super();
this.code = code;
this.name = name;
this.intro = intro;
}
}
开发完实体类后,接着编写映射文件,首先编写Type的映射文件,映射文件的作用是使实体类和数据库中的表形成映射,从而只需要对实体类进行操作就可以改变数据库表中的内容。
Type.hbm.xml映射文件:
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.entity">
<class name="Type" table="type" catalog="zhang">
<id name="id" type="java.lang.Integer">
<column name="id"/>
<generator class="native"/>
</id>
<property name="name" type="java.lang.String">
<column name="name" length="20" not-null="true"></column>
</property>
</class>
</hibernate-mapping>
接着编写ProductInfo的映射文件ProductInfo.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.entity">
<class name="ProductInfo" table="product_info" catalog="zhang">
<id name="id" type="java.lang.Integer">
<column name="id"/>
<generator class="native"/>
</id>
<property name="code" type="java.lang.String">
<column name="code" length="16" not-null="true"/>
</property>
<property name="name" type="java.lang.String">
<column name="name" length="255" not-null="true"/>
</property>
<property name="intro" type="java.lang.String">
<column name="intro"/>
</property>
<!-- 映射实体类ProductInfo到Type的单向多对一的关联 --><!-- 立即加载关联对象 -->
<many-to-one name="type" column="tid" class="Type" lazy="false"></many-to-one>
</class>
</hibernate-mapping>
除此之外,我们还要编写全局配置文件,也就是配置数据库参数,引入映射文件
全局配置文件hibernate.cfg.xml如下
<!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="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/zhang?characterEncoding=utf8</property>
<!-- 设置方言(HQL转换成哪种数据库的sql语句) -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 执行后在控制台显示sql语句-->
<property name="show_sql">true</property>
<property name="connection.username">root</property>
<property name="connection.password">zwj19970923</property>
<!-- 关联映射文件 -->
<mapping resource="com/hibernate/entity/Type.hbm.xml"/>
<mapping resource="com/hibernate/entity/ProductInfo.hbm.xml"/>
</session-factory>
</hibernate-configuration>
接着就可以编写单元测试进行测试了,测试如下:
package com.hibernate.test;
import java.util.Iterator;
import com.hibernate.entity.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class HibernateTest { //单元测试
private Session session;
private Transaction transaction;
private SessionFactory sessionFactory;
@Before
public void init(){//最开始先执行
System.out.println("before");
//加载hibernate配置文件
StandardServiceRegistry registry=new StandardServiceRegistryBuilder().configure().build();
try{
//根据配置文件初始化sessionFactory
sessionFactory=new MetadataSources(registry).buildMetadata().buildSessionFactory();
//创建session
session=sessionFactory.openSession();
//通过session对象开始事务
transaction=session.beginTransaction();
}catch(Exception e){
StandardServiceRegistryBuilder.destroy(registry);
}
}
@Test
public void testManyToOne(){ //单向多对一
ProductInfo pi=(ProductInfo)session.get(ProductInfo.class, 4);
System.out.println("商品名称是:"+pi.getName());
System.out.println("商品类别是:"+pi.getType().getName());
}
@After
public void destory(){//最后才执行
System.out.println("after");
//提交事务
transaction.commit();
//关闭session
session.close();
//关闭sessionFactory
sessionFactory.close();
}
}
测试结果:
控制台输出结果
单向一对多
上面的例子中是单向多对一,那么反过来就是单向一对多了,即同一个类型,它对应着很多商品信息,例如“电脑”类型对应的商品信息有惠普电脑、神州电脑,即可以从一的一端找出所有多的那端信息。
修改Type类,增加一个pi属性,该属性类型为Set<ProductInfo>用于存放对应的商品信息实体对象,并且编写相应的set和get方法(注意:Set不允许集合中有重复值)
package com.hibernate.entity;
import java.util.HashSet;
import java.util.Set;
public class Type {
private int id;
private String name;
private Set pi=new HashSet(); //多个产品信息
public Set getPi() {
return pi;
}
public void setPi(Set pi) {
this.pi = pi;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Type(String name) {
super();
this.name = name;
}
public Type() {
super();
}
}
修改Type.hbm.xml映射文件,增加<set>元素标签。
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.entity">
<class name="Type" table="type" catalog="zhang">
<id name="id" type="java.lang.Integer">
<column name="id"/>
<generator class="native"/>
</id>
<property name="name" type="java.lang.String">
<column name="name" length="20" not-null="true"></column>
</property>
<!-- 配置单向一对多 --><!-- inverse控制权反转,让多的那端(Product_Info)管理关联关系提高性能 --><!-- 采用级联删除方法,删除type表记录的同时会将product_info表中与type记录相关联的记录一起删除 -->
<set name="pi" lazy="false" inverse="true" cascade="delete">
<key column="tid"/>
<one-to-many class="ProductInfo"/>
</set>
</class>
</hibernate-mapping>
注意:<set>标签中的cascade配置了delete是级联删除方法,这样配置后删除type表记录时将连同product_info表中与type记录相关联的记录一并删除
ProductInfo实体类以及它的映射文件保持不变
测试:
编写测试单元,代码如下:
package com.hibernate.test;
import java.util.Iterator;
import com.hibernate.entity.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class HibernateTest { //单元测试
private Session session;
private Transaction transaction;
private SessionFactory sessionFactory;
@Before
public void init(){//最开始先执行
System.out.println("before");
//加载hibernate配置文件
StandardServiceRegistry registry=new StandardServiceRegistryBuilder().configure().build();
try{
//根据配置文件初始化sessionFactory
sessionFactory=new MetadataSources(registry).buildMetadata().buildSessionFactory();
//创建session
session=sessionFactory.openSession();
//通过session对象开始事务
transaction=session.beginTransaction();
}catch(Exception e){
StandardServiceRegistryBuilder.destroy(registry);
}
}
@Test
public void testOneToMany(){ //单向一对多
Type type=(Type)session.get(Type.class, 2);//获取类型为"电脑"的实体
Iterator it=type.getPi().iterator();
System.out.println(type.getName()+"类型的商品有:");
while(it.hasNext()){
ProductInfo pi=(ProductInfo)it.next();
System.out.println(pi.getName());//显示类型为"电脑"的商品名称
}
}
@After
public void destory(){//最后才执行
System.out.println("after");
//提交事务
transaction.commit();
//关闭session
session.close();
//关闭sessionFactory
sessionFactory.close();
}
}
测试结果:
数据库中的表
控制台输出结果:
双向多对一
双向多对一和双向一对多是相同的,单向一对多和单向多对一的集合便是双向多对一(双向一对多)这里主要实现双向多对一的对数据的增加删除修改等操作
配置文件和实体类以及映射文件不需要改变,编写测试文件,实现对数据的添加、删除、更新
package com.hibernate.test;
import java.util.Iterator;
import com.hibernate.entity.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class HibernateTest { //单元测试
private Session session;
private Transaction transaction;
private SessionFactory sessionFactory;
@Before
public void init(){//最开始先执行
System.out.println("before");
//加载hibernate配置文件
StandardServiceRegistry registry=new StandardServiceRegistryBuilder().configure().build();
try{
//根据配置文件初始化sessionFactory
sessionFactory=new MetadataSources(registry).buildMetadata().buildSessionFactory();
//创建session
session=sessionFactory.openSession();
//通过session对象开始事务
transaction=session.beginTransaction();
}catch(Exception e){
StandardServiceRegistryBuilder.destroy(registry);
}
}
@Test
public void ManyToOneSave(){ //双向多对一插入数据
Type ty=new Type("打印机");
ProductInfo pi1=new ProductInfo("10034","惠普打印机","惠普打印机-一体机");
ProductInfo pi2=new ProductInfo("10037","中国打印机","中国打印机-一体机");
//设置关联关系
pi1.setType(ty);
pi2.setType(ty);
//向数据表一插入一端的数据对象
session.save(ty);
//再插入多的一端的数据对象
session.save(pi1);
session.save(pi2);
}
@Test
public void manyToOneUpdate(){ //双向多对一更新数据
ProductInfo pi=session.get(ProductInfo.class, 9);
Type ty=session.get(Type.class, 1);
pi.setType(ty);
session.save(ty);
}
@Test
public void manyToOneUpdateDelete(){ //采用级联方法删除数据(双向多对一删除数据)
Type type=session.get(Type.class, 7);
session.delete(type);
}
@After
public void destory(){//最后才执行
System.out.println("after");
//提交事务
transaction.commit();
//关闭session
session.close();
//关闭sessionFactory
sessionFactory.close();
}
}
双向多对多
数据库中表之间也有双向多对多的关系,例如管理员表和系统功能表是多对多关系,即一个管理员可以使用多个系统功能,一个系统功能能被多个管理员使用,这种多对多的关系在数据库中是不能直接实现的,必须建立一个中间表,该表的字段包括管理员表和系统功能表的主键,而它们两者与中间表形成一对多关系,进而间接的形成多对多关系,系统功能表(function)和管理员表(admin_info)以及中间表(power)的关系如下,建立了该关系,就等价于系统功能表和管理员表之间形成多对多的关联关系。
接着开发实体类Function以及Admin,分别对应功能表和管理员表。
Admin.java
package com.hibernate.entity;
import java.util.HashSet;
import java.util.Set;
public class Admin {
private int id;//管理员id
private String name;//管理员姓名
//关联属性
private Set function=new HashSet();
public Admin(String name) {
this.name = name;
}
public Admin() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set getFunction() {
return function;
}
public void setFunction(Set function) {
this.function = function;
}
}
Function.java
package com.hibernate.entity;
import java.util.HashSet;
import java.util.Set;
public class Function {
private int id;//功能id
private String name;//功能名称
//关联属性
private Set admins=new HashSet();
public Function(String name) {
this.name = name;
}
public Function() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set getAdmins() {
return admins;
}
public void setAdmins(Set admins) {
this.admins = admins;
}
}
接着编写这两个实体类的映射文件
Admin.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.entity">
<class name="Admin" table="admin_info" catalog="zhang">
<id name="id" type="java.lang.Integer">
<column name="id"/>
<generator class="native"/>
</id>
<property name="name" type="java.lang.String">
<column name="name" length="20" not-null="true"/>
</property>
<set name="function" table="power">
<key column="aid" not-null="true"/>
<many-to-many column="fid" class="Function"/>
</set>
</class>
</hibernate-mapping>
Function.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.entity">
<class name="Function" table="function" catalog="zhang">
<id name="id" type="java.lang.Integer">
<column name="id"/>
<generator class="native"/>
</id>
<property name="name" type="java.lang.String">
<column name="name" length="20" not-null="true"/>
</property>
<set name="admins" table="power">
<key column="fid" not-null="true"/>
<many-to-many column="aid" class="Admin"/>
</set>
</class>
</hibernate-mapping>
在全局配置文件中增加这两个映射文件
<!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="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/zhang?characterEncoding=utf8</property>
<!-- 设置方言(HQL转换成哪种数据库的sql语句) -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<property name="connection.username">root</property>
<property name="connection.password">zwj19970923</property>
<!-- 关联映射类 -->
<mapping resource="com/hibernate/entity/Type.hbm.xml"/>
<mapping resource="com/hibernate/entity/ProductInfo.hbm.xml"/>
<mapping resource="com/hibernate/entity/UserInfo.hbm.xml"/>
<mapping resource="com/hibernate/entity/Admin.hbm.xml"/>
<mapping resource="com/hibernate/entity/Function.hbm.xml"/>
</session-factory>
</hibernate-configuration>
配置完成后就可以编写单元测试了,测试代码如下
package com.hibernate.test;
import java.util.Iterator;
import com.hibernate.entity.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class HibernateTest { //单元测试
private Session session;
private Transaction transaction;
private SessionFactory sessionFactory;
@Before
public void init(){//最开始先执行
System.out.println("before");
//加载hibernate配置文件
StandardServiceRegistry registry=new StandardServiceRegistryBuilder().configure().build();
try{
//根据配置文件初始化sessionFactory
sessionFactory=new MetadataSources(registry).buildMetadata().buildSessionFactory();
//创建session
session=sessionFactory.openSession();
//通过session对象开始事务
transaction=session.beginTransaction();
}catch(Exception e){
StandardServiceRegistryBuilder.destroy(registry);
}
}
@Test
public void ManyToManySave(){ //双向多对多保存数据
//新建两个管理员对象
Admin ad_1=new Admin("管理员1");
Admin ad_2=new Admin("管理员2");
//创建三个功能对象
Function f1=new Function("订单管理");
Function f2=new Function("用户账号管理");
Function f3=new Function("商品信息管理");
//关联管理员和功能对象
ad_1.getFunction().add(f1);
ad_1.getFunction().add(f2);
ad_2.getFunction().add(f2);
ad_2.getFunction().add(f3);
//保存功能对象和管理员对象
session.save(f1);
session.save(f2);
session.save(f3);
session.save(ad_1);
session.save(ad_2);
}
@After
public void destory(){//最后才执行
System.out.println("after");
//提交事务
transaction.commit();
//关闭session
session.close();
//关闭sessionFactory
sessionFactory.close();
}
}
执行上面的单元测试,可以看见数据库中admin_info表增加了两项记录,分别为管理员1和管理员2
function表则增加了三项记录,分别为订单管理、用户账号管理、商品信息管理
power则增加了四项记录,用于关联管理员表和功能表之间形成多对多联系
其中我们可以看到,管理员id为1的管理员拥有功能id为3和4的功能,管理员和功能之间形成一对多的关联,而管理员id为2的管理员拥有功能id为4和5的功能,功能id为4的功能分别被两个管理员所拥有,所以可见管理员和功能之间多对多的关系表现出来了。
基于外键的双向一对一
一对一是一对多的一个特例,例如管理员信息表和管理员详细信息表是一对一关系,一对一有两种实现方式,一种是基于外键的一对一,一种是基于主键的一对一,这里主要讲讲基于外键的一对一关联。
外键可以存放在一对一的任意一端,在存放外键的一端增加<many-to-one>元素,并在该元素中增加unique="true"属性,该属性表示多的一方也必须唯一,并使用name属性来指定关联属性的属性名,在另一端需要使用<one-to-one>元素,同样使用name属性来指定关联属性的属性名
编写实体类AdminDetail.java以及AdminInfo.java分别对应admin_info表和admin_detail表
package com.hibernate.entity;
public class AdminInfo {
private int id;
private String name;
private String pwd;
//关联属性
private AdminDetail ad;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public AdminDetail getAd() {
return ad;
}
public void setAd(AdminDetail ad) {
this.ad = ad;
}
public AdminInfo() {
super();
// TODO Auto-generated constructor stub
}
public AdminInfo(String name, String pwd) {
super();
this.name = name;
this.pwd = pwd;
}
}
package com.hibernate.entity;
public class AdminDetail {
private int id;
private String realName;
private String address;
//关联属性
private AdminInfo ai;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public AdminInfo getAi() {
return ai;
}
public void setAi(AdminInfo ai) {
this.ai = ai;
}
public AdminDetail() {
super();
// TODO Auto-generated constructor stub
}
public AdminDetail(String realName, String address) {
super();
this.realName = realName;
this.address = address;
}
}
接着编写映射文件AdminInfo.hbm.xml和AdminDetail.hbm.xml(注意:在存放外键的一端增加<many-to-one>元素,并在该元素中增加unique="true"属性,该属性表示多的一方也必须唯一)
AdminInfo.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.entity">
<class name="AdminInfo" table="admin_info" catalog="zhang">
<id name="id" type="java.lang.Integer">
<column name="id"/>
<generator class="native"/>
</id>
<property name="name" type="java.lang.String">
<column name="name" length="255" not-null="true"/>
</property>
<property name="pwd" type="java.lang.String">
<column name="pwd" length="16" not-null="true"/>
</property>
<!-- 使用many-to-one实现一对一映射 -->
<many-to-one name="ad" class="AdminDetail" column="did" unique="true"></many-to-one>
</class>
</hibernate-mapping>
AdminDetail.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.entity">
<class name="AdminDetail" table="admin_detail" catalog="zhang">
<id name="id" type="java.lang.Integer">
<column name="id"/>
<generator class="native"/>
</id>
<property name="realName" type="java.lang.String">
<column name="realName" length="255" not-null="true"/>
</property>
<property name="address" type="java.lang.String">
<column name="address" length="20" />
</property>
<!-- 使用one-to-one配置一对一 -->
<one-to-one name="ai" class="AdminInfo" property-ref="ad"></one-to-one>
</class>
</hibernate-mapping>
编写单元测试,测试代码如下
package com.hibernate.test;
import com.hibernate.entity.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class HibernateTest { //单元测试
private Session session;
private Transaction transaction;
private SessionFactory sessionFactory;
@Before
public void init(){//最开始先执行
System.out.println("before");
//加载hibernate配置文件
StandardServiceRegistry registry=new StandardServiceRegistryBuilder().configure().build();
try{
//根据配置文件初始化sessionFactory
sessionFactory=new MetadataSources(registry).buildMetadata().buildSessionFactory();
//创建session
session=sessionFactory.openSession();
//通过session对象开始事务
transaction=session.beginTransaction();
}catch(Exception e){
StandardServiceRegistryBuilder.destroy(registry);
}
}
@Test
public void oneToOneSave(){
//创建两个管理员
AdminInfo ai_1=new AdminInfo("管理员1","1234");
AdminInfo ai_2=new AdminInfo("管理员2","1234555");
//创建两个管理员详细信息
AdminDetail ad_1=new AdminDetail("张三","广东广州");
AdminDetail ad_2=new AdminDetail("李四","广东惠州");
//管理员设置详细信息
ai_1.setAd(ad_1);
ai_2.setAd(ad_2);
ad_1.setAi(ai_1);
ad_2.setAi(ai_2);
//保存数据
session.save(ad_1);
session.save(ad_2);
session.save(ai_1);
session.save(ai_2);
}
@After
public void destory(){//最后才执行
System.out.println("after");
//提交事务
transaction.commit();
//关闭session
session.close();
//关闭sessionFactory
sessionFactory.close();
}
}
测试:
执行单元测试,可以看见数据库中管理员信息表和管理员详细信息表增加了几项记录
admin_info表
admin_detail表
它们之间形成一对一关系
基于主键的双向一对一
修改admin_info表,删除外键约束,再删除did外键字段,在admin_info表的主键上添加外键约束,外键是admin_detail的主键,也是admin_info的主键,如下图所示
实体类保持不变,修改AdminInfo.hbm.xml配置文件,在基于主键一对一的情况下,有外键一方的主键生成策略一定要是foreign即<generator class="foreign"></generator>然后必须在该元素中配置子标签<param name="property">ad</param>参数property表示生成主键值时所根据的对象【一对一时关联的对方】这里是AdminInfo中的关联属性ad,表示生成主键是根据关联属性ad来生成的,除此之外,还要使用<one-to-one>实现一对一关联关系,并且要加上constrained="true"属性
AdminInfo.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.entity">
<class name="AdminInfo" table="admin_info" catalog="zhang">
<id name="id" type="java.lang.Integer">
<column name="id"/>
<!--
当使用基于主键的一对一映射时,有外键方的主键生成策略一定要是foreign
参数property:生成主键值时所根据的对象【一对一时关联的对方】
-->
<generator class="foreign">
<param name="property">ad</param>
</generator>
</id>
<property name="name" type="java.lang.String">
<column name="name" length="255" not-null="true"/>
</property>
<property name="pwd" type="java.lang.String">
<column name="pwd" length="16" not-null="true"/>
</property>
<!-- 使用one-to-one实现一对一关联关系 -->
<one-to-one name="ad" class="AdminDetail" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
AdminDetail.hbm.xml配置文件同样使用<one-to-one>标签配置一对一,因为Admin_detail表主键是自增生成,所以该配置文件主键生成策略不需要改变
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.entity">
<class name="AdminDetail" table="admin_detail" catalog="zhang">
<id name="id" type="java.lang.Integer">
<column name="id"/>
<generator class="native"/>
</id>
<property name="realName" type="java.lang.String">
<column name="realName" length="255" not-null="true"/>
</property>
<property name="address" type="java.lang.String">
<column name="address" length="20" />
</property>
<!-- 使用one-to-one配置一对一 --><!-- 采用级联方式删除(删除主表的数据,副表与之相关联的数据也会被删除) -->
<one-to-one name="ai" class="AdminInfo" cascade="delete"></one-to-one>
</class>
</hibernate-mapping>
实体类AdminInfo.java以及AdminDetail.java如下
package com.hibernate.entity;
public class AdminDetail {
private int id;
private String realName;
private String address;
//关联属性
private AdminInfo ai;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public AdminInfo getAi() {
return ai;
}
public void setAi(AdminInfo ai) {
this.ai = ai;
}
public AdminDetail() {
super();
// TODO Auto-generated constructor stub
}
public AdminDetail(String realName, String address) {
super();
this.realName = realName;
this.address = address;
}
}
package com.hibernate.entity;
public class AdminInfo {
private int id;
private String name;
private String pwd;
//关联属性
private AdminDetail ad;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public AdminDetail getAd() {
return ad;
}
public void setAd(AdminDetail ad) {
this.ad = ad;
}
public AdminInfo() {
super();
// TODO Auto-generated constructor stub
}
public AdminInfo(String name, String pwd) {
super();
this.name = name;
this.pwd = pwd;
}
}
编写单元测试,主要实现对管理员数据的插入、修改、删除、查询。
package com.hibernate.test;
import com.hibernate.entity.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class HibernateTest { //单元测试
private Session session;
private Transaction transaction;
private SessionFactory sessionFactory;
@Before
public void init(){//最开始先执行
System.out.println("before");
//加载hibernate配置文件
StandardServiceRegistry registry=new StandardServiceRegistryBuilder().configure().build();
try{
//根据配置文件初始化sessionFactory
sessionFactory=new MetadataSources(registry).buildMetadata().buildSessionFactory();
//创建session
session=sessionFactory.openSession();
//通过session对象开始事务
transaction=session.beginTransaction();
}catch(Exception e){
StandardServiceRegistryBuilder.destroy(registry);
}
}
@Test
public void testOneToOneSave(){
//创建一个管理员信息
AdminInfo ai=new AdminInfo("管理员1","1234");
//创建一个管理员详细信息
AdminDetail ad=new AdminDetail("张三","广东广州");
//将管理员1和管理员详细信息关联
ai.setAd(ad);
//保存管理员信息
session.save(ai);
}
@Test
public void testOneToOneRead(){
//读取管理员信息
AdminInfo ai=(AdminInfo)session.get(AdminInfo.class, 1);
System.out.println(ai.getName());
//从管理员信息中读取管理员详细信息
AdminDetail ad=(AdminDetail)ai.getAd();
System.out.println(ad.getRealName());
}
@Test
public void testOneToOneUpdate(){
//读取管理员详细信息
AdminDetail ad=session.get(AdminDetail.class, 1);
//获取与管理员详细信息关联的管理员信息
AdminInfo ai=(AdminInfo)ad.getAi();
System.out.println(ai.getName());
//更改管理员信息中的管理员名称
ai.setName("管理员10");
//更新
session.save(ai);
}
@Test
public void testOneToOneDelete(){
//采用级联方法删除管理信息
AdminDetail ad=session.get(AdminDetail.class, 1);
session.delete(ad);
}
@After
public void destory(){//最后才执行
System.out.println("after");
//提交事务
transaction.commit();
//关闭session
session.close();
//关闭sessionFactory
sessionFactory.close();
}
}
测试:
插入数据:
读取数据:
修改数据:
删除数据:
采用级联删除法,删除admin_detail表记录的同时会删除admin_info表与之关联的记录