JPA学习(六) 单向多对一
记录 单向多对一
目录
1.单向多对一
单向多对一 (many to one) (一方拿不了多方 但是多方可以拿一方)
比如:产品对应产品类型(其实就是可以理解为创建外键 处理外键的值)
@ManyToOne //多对一 多个产品对应一个产品类型 所以这个是配置在多方的对象上的 默认配置@ManyToOne h会自动建立外键, 外键id名称=属性名_id
不能使用@clumn (name=...) 来配置外键名称 得使用 @JoinColumn(name = "dir_id") //用这个注解来配置
1.多方的代码
@Entity //产品 多方
@Table(name = "t_goods")
public class Goods {
@Id
@GeneratedValue
private Long id;
private String goodsName;
//一方的对象
// @ManyToOne //d多对一 多个产品对应一个产品类型 默认配置@ManyToOne h会自动建立外键, 外键id名称=属性名_id
@ManyToOne(fetch = FetchType.LAZY) //延迟加载
//默认立即加载
//不能使用@clumn (name=...) 来配置外键名称
@JoinColumn(name = "type_id") //用这个注解来配置外键 让他生成可以查询到一方主键的外键
private GoodsType goodsType; //这里不能默认new一个 否则会报错
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
public GoodsType getGoodsType() {
return goodsType;
}
public void setGoodsType(GoodsType goodsType) {
this.goodsType = goodsType;
}
@Override
public String toString() {
return "Goods{" +
"id=" + id +
", goodsName='" + goodsName + '\'' +
'}';
}
}
注意 使用了一个延迟加载 提升效率 (后面稍微稍微说一下)
2.一方代码
@Entity //产品订单 一方
public class GoodsType {
@Id
@GeneratedValue
private Long tid;
private String tname;
@Override
public String toString() {
return "GoodsType{" +
"tid=" + tid +
", tname='" + tname + '\'' +
'}';
}
public Long getTid() {
return tid;
}
public void setTid(Long tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
}
3.测试类
public class MtoOTest {
@Before
//实例化多方 和 一方的正确示范
public void mtoTest() {
EntityManager entityManager = JPAUtil.getEntityManager();
entityManager.getTransaction().begin();
//多方
Goods goods1 = new Goods();
goods1.setGoodsName("apple");
Goods goods2 = new Goods();
goods2.setGoodsName("banana FAQ");
//一方
GoodsType goodsType = new GoodsType();
goodsType.setTname("fruit");
//建立多方到一方的关系 本质就是处理外键的值
// goods1.getGoodsType.setTypeId(goodsType.getId);
goods1.setGoodsType(goodsType);
goods2.setGoodsType(goodsType);
//正确顺序: 先保存一方 再 保存多方
entityManager.persist(goodsType); //当goodstype 持久化后 存入到表里面 然后将goods的外键进行了更新
//提交
entityManager.persist(goods1);
entityManager.persist(goods2);
entityManager.getTransaction().commit();
entityManager.close();
}
@Test //获取多方的数据
public void getOneTest() {
EntityManager entityManager = JPAUtil.getEntityManager();
Goods goods = entityManager.find(Goods.class, 1L);
System.out.println(goods.toString());
System.out.println(goods.getGoodsType()); //获取关联对象 懒加载这里才会发数据
/* fetch 抓取策略 立即加载策略
* select
goods0_.id as id1_5_0_,
goods0_.goodsName as goodsNam2_5_0_,
goods0_.goodsType_tid as goodsTyp3_5_0_,
goodstype1_.tid as tid1_0_1_,
goodstype1_.tname as tname2_0_1_
from
t_goods goods0_
left outer join
GoodsType goodstype1_
on goods0_.goodsType_tid=goodstype1_.tid
where
goods0_.id=?
Goods{id=1, goodsName='apple'goodsType=fruit\'}
* 多方 左外连接 一方 获得数据
* */
/* fetch 抓取策略 延迟加载 @ManyToOne(fetch = FetchType.LAZY) //延迟加载
Hibernate:
select
goods0_.id as id1_5_0_,
goods0_.goodsName as goodsNam2_5_0_,
goods0_.goodsType_tid as goodsTyp3_5_0_
from
t_goods goods0_
where
goods0_.id=?
Goods{id=1, goodsName='apple'}
Hibernate:
select
goodstype0_.tid as tid1_0_0_,
goodstype0_.tname as tname2_0_0_
from
GoodsType goodstype0_
where
goodstype0_.tid=?
GoodsType{tid=1, tname='fruit'}
*/
}
4.运行结果
执行的是 getOneTest() Test方法
可以看到 第一步在创建entityManager的时候会初始化表:
表对应的建表语句:
goodtype(一方):
CREATE TABLE `goodstype` (
`tid` bigint(20) NOT NULL AUTO_INCREMENT,
`tname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`tid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
good(多方): 可以看到有添加外键 并将typeid和 外键的tid进行绑定
CREATE TABLE `t_goods` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`goodsName` varchar(255) DEFAULT NULL,
`type_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_iq8i1e6oxrmldi7ac5jx0v2sx` (`type_id`),
CONSTRAINT `FK_iq8i1e6oxrmldi7ac5jx0v2sx` FOREIGN KEY (`type_id`) REFERENCES `goodstype` (`tid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
代码往下走 看得到添加数据:(先添加一方,再添加多方)
到提交事务之前,做了如下数据库操作:可以看到 将一方的数据保存了 ,再保存了 多方的数据
Hibernate: insert into GoodsType (tname) values (?)
Hibernate: insert into t_goods (goodsName, type_id) values (?, ?)
Hibernate: insert into t_goods (goodsName, type_id) values (?, ?)
但是数据库还没有
事务提交后:
,
这里只执行了三行sql,(在单向一对多的时候实现相同效果需要五条sql 每次插入多方数据会update一次)
然后到查询数据
当查询完打印goods时,
执行的语句是:
Hibernate:
select
goods0_.id as id1_5_0_,
goods0_.goodsName as goodsNam2_5_0_,
goods0_.type_id as type_id3_5_0_
from
t_goods goods0_
where
goods0_.id=?
没有去专门去取对应的产品类型。
再往下走 获取 getType,这个时候才出现了新的查询语句,查询对应的goodsType,
Hibernate:
select
goodstype0_.tid as tid1_0_0_,
goodstype0_.tname as tname2_0_0_
from
GoodsType goodstype0_
where
goodstype0_.tid=?
原因是在配置了懒加载,就是在多方属性配置@ManyToOne(fetch = FetchType.LAZY)。
如果我把懒加载去掉,可以看到执行的语句是一次性执行完毕的
select
goods0_.id as id1_5_0_,
goods0_.goodsName as goodsNam2_5_0_,
goods0_.goodsType_tid as goodsTyp3_5_0_,
goodstype1_.tid as tid1_0_1_,
goodstype1_.tname as tname2_0_1_
from
t_goods goods0_
left outer join
GoodsType goodstype1_ on goods0_.goodsType_tid=goodstype1_.tid
where
goods0_.id=?
这样耗内存,所以一般都不这么使用。
2.注意点
错误保存:
多方对象的关联一方的对象 不要默认实例化
因为实例化后没有id和外键 否则报错 TransientPropertyValueException
正确保存:
一次性保存一方 同时保存2个多方 比如:保存一个产品类型 ,两个产品
先保存一方 也就是外键的对应的表的数据先存入 后存入 多方的数据 也就是拥有外键的数据存入
(和数据库的数据存入顺序一致)
fetch 抓取 (ManyToOneTest)
1.默认立即加载
@ManyToOne
2.延迟加载
@ManyToOne(fetch = FetchType.LAZY) //延迟加载
获取持久状态对象后 还需要获取关联对象(比如getGoodsType()),此时才会发出真正的sql 获取值
好处:
提高性能,但是如果把entityManager提前关闭 会出现延迟加载 初始化异常 LazyInitzationException : no session
看测试代码中的 他的立即加载 是会用左外连接将数据全部查询出来
而懒加载是你要查询什么就查询什么 不需要的就不会一次性查出来 这样提高效率
可以通过多方.get一方是否为null 来判断是否有外键的关联
[email protected](name = "type_id")
用这个注解来配置外键 让他生成可以查询到一方主键的外键
上一篇: java nio
下一篇: java nio 示例代码