深入解析Java的Hibernate框架中的一对一关联映射
作者:cxshun 发布时间:2022-08-07 22:23:39
作为一个ORM框架,hibernate肯定也需要满足我们实现表与表之间进行关联的需要。hibernate在关联方法的实现很简单。下面我们先来看看一对一的做法:
不多说了,我们直接上代码:
两个实体类,TUser和TPassport:
public class TUser implements Serializable{
private static final long serialVersionUID = 1L;
private int id;
private int age;
private String name;
private TPassport passport;
//省略Get/Set方法
}
public class TPassport implements Serializable{
private static final long serialVersionUID = 1L;
private int id;
private String serial;
private int expiry;
private TUser user;
//省略Get/Set方法
}
下面我们看一下映射文件有什么不同:
<hibernate-mapping package="org.hibernate.tutorial.domain4">
<class name="TUser" table="USER4">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="name" type="java.lang.String" column="name"/>
<property name="age" type="java.lang.Integer" column="age"/>
<one-to-one name="passport" class="TPassport"
cascade="all" outer-join="true" />
</class>
</hibernate-mapping>
这里我们看到有一个新标签,one-to-one,它表明当前类与所对应的类是一对一的关系,cascade是级联关系,all表明无论什么情况都进行级联,即当对TUser类进行操作时,TPassport也会进行相应的操作,outer-join是指是否使用outer join语句。
我们再看另外一个TPassport的映射文件:
<hibernate-mapping package="org.hibernate.tutorial.domain4">
<class name="TPassport" table="passport4">
<id name="id" column="id">
<generator class="foreign" >
<param name="property">user</param>
</generator>
</id>
<property name="serial" type="java.lang.String" column="serial"/>
<property name="expiry" type="java.lang.Integer" column="expiry"/>
<one-to-one name="user" class="TUser" constrained="true" />
</class>
</hibernate-mapping>
这里我们重点看到generator的class值,它为foreign表明参照外键,而参照哪一个是由param来进行指定,这里表明参照User类的id。而one-to-one标签中多了一个constrained属性,它是告诉hibernate当前类存在外键约束,即当前类的ID根据TUser的ID进行生成。
下面我们直接上测试类,这次测试类没有用JUnit而是直接Main方法来了:
public static void main(String[] args) {
Configuration cfg = new Configuration().configure();
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
TUser user = new TUser();
user.setAge(20);
user.setName("shunTest");
TPassport passport = new TPassport();
passport.setExpiry(20);
passport.setSerial("123123123");
passport.setUser(user);
user.setPassport(passport);
session.save(user);
session.getTransaction().commit();
}
代码很简单,就不说了。我们主要看这里:
session.save(user);
这里为什么我们只调用一个save呢,原因就在我们的TUser映射文件中的cascade属性,它被设为all,即表明当我们对TUser进行保存,更新,删除等操作时,TPassport也会进行相应的操作,所以这里我们不用写session.save(passport)。我们看到后台:
Hibernate: insert into USER4 (name, age) values (?, ?)
Hibernate: insert into passport4 (serial, expiry, id) values (?, ?, ?)
Hibernate: 它打印出两个语句,证明hibernate确定帮我们做了这个工作。
下面我们再来一个测试类,测试查询:
public static void main(String[] args) {
Configuration cfg = new Configuration().configure();
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
TUser user = (TUser)session.load(TUser.class,new Integer(3));
System.out.println(user.getName()+":"+user.getPassport().getSerial());
}
这里我们通过查询出TUser类,取到TPassport对象。
我们可以看到hibernate的SQL语句是:
Hibernate:
select tuser0_.id as id0_1_, tuser0_.name as name0_1_, tuser0_.age as age0_1_, tpassport1_.id as id1_0_, tpassport1_.serial as serial1_0_, tpassport1_.expiry as expiry1_0_ from USER4 tuser0_ left outer join passport4 tpassport1_ on tuser0_.id=tpassport1_.id where tuser0_.id=?
我们看到语句当中有left outer join,这是因为我们前面在one-to-one当中设了outer-join="true",我们试着把它改成false,看到SQL语句如下:
Hibernate:
select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.age as age0_0_ from USER4 tuser0_ where tuser0_.id=?
Hibernate:
select tpassport0_.id as id1_0_, tpassport0_.serial as serial1_0_, tpassport0_.expiry as expiry1_0_ from passport4 tpassport0_ where tpassport0_.id=?
现在是分成两条来查了,根据第一条查出的ID再到第二条查出来。
也许很多人会问为什么测试的时候不用TPassport查出TUser呢,其实也可以的,因为它们是一对一的关系,谁查谁都是一样的。
外键关联
现在我们看一下通过外键来进行关联的一对一关联。
还是一贯的直接上例子:我们写了两个实体类,TGroup和TUser
public class TGroup implements Serializable{
private static final long serialVersionUID = 1L;
private int id;
private String name;
private TUser user;
//省略Get/Set方法
}
public class TUser implements Serializable{
private static final long serialVersionUID = 1L;
private int id;
private int age;
private String name;
private TGroup group;
//省略Get/Set方法
}
实体类完了我们就看一下映射文件:
<hibernate-mapping package="org.hibernate.tutorial.domain5">
<class name="TUser" table="USER5">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="name" type="java.lang.String" column="name"/>
<property name="age" type="java.lang.Integer" column="age"/>
<many-to-one name="group" class="TGroup" column="group_id" unique="true" />
</class>
</hibernate-mapping>
这里我们看到是用many-to-one标签而不是one-to-one,为什么呢?
这里以前用的时候也没多在注意,反正会用就行,但这次看了夏昕的书终于明白了,实际上这种通过外键进行关联方式只是多对一的一种特殊方式而已,我们通过unique="true"限定了它必须只能有一个,即实现了一对一的关联。
接下来我们看一下TGroup的映射文件:
<hibernate-mapping package="org.hibernate.tutorial.domain5">
<class name="TGroup" table="group5">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="name" type="java.lang.String" column="name"/>
<one-to-one name="user" class="TUser" property-ref="group" />
</class>
</hibernate-mapping>
这里,注意,我们又用到了one-to-one,表明当前的实体和TUser是一对一的关系,这里我们不用many-to-one,而是通过one-to-one指定了TUser实体中通过哪个属性来关联当前的类TGroup。这里我们指定了TUser是通过group属性和Tuser进行关联的。property-ref指定了通过哪个属性进行关联。
下面我们看测试类:
public class HibernateTest {
public static void main(String[] args) {
Configuration cfg = new Configuration().configure();
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
TGroup group = new TGroup();
group.setName("testGroup");
TUser user = new TUser();
user.setAge(23);
user.setName("test");
user.setGroup(group);
group.setUser(user);
session.save(group);
session.save(user);
session.getTransaction().commit();
session.close();
}
}
注意,这次我们的代码中需要进行两次的保存,因为它们对各自都有相应的对应,只保存一个都不会对另外一个有什么操作。所以我们需要调用两次保存的操作。最后进行提交。
hibernate打印出语句:
Hibernate: insert into group5 (name) values (?)
Hibernate: insert into USER5 (name, age, group_id) values (?, ?, ?)
这说明我们正确地存入了两个对象值。
我们写多一个测试类进行查询:
public static void main(String[] args) {
Configuration cfg = new Configuration().configure();
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
TUser user = (TUser)session.load(TUser.class,new Integer(1));
System.out.println("From User get Group:"+user.getGroup().getName());
TGroup group = (TGroup)session.load(TGroup.class,new Integer(1));
System.out.println("From Group get User:" + group.getUser().getName());
session.close();
}
我们都可以得到正确的结果,这表明我们可以通过两个对象拿出对方的值,达到了我们的目的。
这个例子中用到的TGroup和TUser只是例子而已,实际上现实生活中的user一般都对应多个group。


猜你喜欢
- @Autowired注解注入的xxxMapper报错项目场景Mybatis-Plus测试问题描述在Mybatis-Plus场景的测试中发现,
- 前言数据驱动测试是相同的测试脚本使用不同的测试数据执行,测试数据和测试行为完全分离。数据驱动是做自动化测试中很重要的一部分,数据源的方案也是
- 本文实例讲述了C#针对xml的基本操作及保存配置文件应用,分享给大家供大家参考。具体方法如下:引言:这里首先介绍了xml的基本操作,后面写了
- 本文实例为大家分享了Android实现指针刻度转盘的具体代码,供大家参考,具体内容如下一. 先上个效果图,实现如图所示刻度转盘和2个文本的绘
- 线索二叉树的意义对于一个有n个节点的二叉树,每个节点有指向左右孩子的指针域。其中会出现n+ 1个空指针域,这些空间不储存任何事物,浪费着内存
- sftp简介sftp是Secure File Transfer Protocol的缩写,安全文件传送协议。可以为传输文件提供一种安全的网络的
- 面试题1:说说什么分布式事务?解释一下什么是CAP?现在互联网开发多使用微服务架构,一个简单的操作,在服务端可能就是由多个服务和数据库实例协
- 在项目中经常要用到将字符串解析成Locale,但是没有一个比较好用的类。java本身提供了3个构造函数,但是实际使用过程中,需要自己解析,比
- SpringBoot控制配置类加载顺序@Configuration配置被加载进容器的方式大体上可分为3种手动:构建ApplicationCo
- 假设下面是你的视频网站链接列表,如果别人想爬取你的数据十分轻松,看规则就知道数据库是序列自增的http://www.xxxx.com/vid
- maven简介及优势 maven是一个项目构建和管理的工具,提供了帮助管理 构建、文档、报告
- 在 Java 中将 Object 转换为 Int我们可以使用 Object 类来引用我们在 Java 中不知道其类型的任
- Eureka 采用 CS(Client/Server,客户端/服务器) 架构,它包括以下两大组件:Eureka Server:Eureka
- 摘要:最近有一个需求,为客户提供一些Restful API 接口,QA使用postman进行测试,但是postman的测试接口与java调用
- Java CharArrayReader流一、CharArrayReader流定义API说明:该类实现了一个可用作字符输入流的字符缓冲区,即
- Thread-per-Message模式(这项工作就交给你了)当你很忙碌的时候,这个时候公司楼下有个快递,于是你委托你的同事帮你拿一下你的快
- 一、简介WorkManager 用于处理 Android 后台任务。我们只需要设置好任务内容、何时执行,剩下的工作就可以完全交给系统处理。它
- 对于Android 的手势不光在软件中会经常用到,比如浏览器中的翻
- 1. mybatis-plus开启二级缓存spring: datasource: type: com.alibaba.druid
- 主要功能:勾选子节点的checkbox,右边会动态加载该节点的信息,出现TextBox让用户填写节点的值,点击保存按钮将文本框的值保存到对应