JPA定义实体之间的关系有如下几种:
@OneToOne @ManyToOne @OneToMany @ManyToMany
在定义它们的时候可以通过fetch属性指定加载方式,有两个值:
FetchType.LAZY:延迟加载
FetchType.EAGER:急加载
Hibernate单端关联懒加载策略:即在<one-to-one>/<many-to-one>标签上可以配置
懒加载策略。可以取值为:false/proxy/no-proxy
fetch策略
@OneToMany(mappedBy="image",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
@Fetch(value=FetchMode.SUBSELECT)
两者比较:
两者都是设定关联对象的加载策略。前者是JPA标准的通用加载策略注解属性,
后者是Hibernate自有加载策略注解属性。
FetchType可选值意义与区别如下:
FetchType.LAZY: 懒加载,在访问关联对象的时候加载(即从数据库读入内存)
FetchType.EAGER:立刻加载,在查询主对象的时候同时加载关联对象。
FetchMode可选值意义与区别如下:
@Fetch(FetchMode.JOIN): 始终立刻加载,使用外连(outer join)查询的同时加载关联对象,忽略FetchType.LAZY设定。
@Fetch(FetchMode.SELECT) :默认懒加载(除非设定关联属性lazy=false),当访问每一个关联对象时加载该对象,会累计产生N+1条sql语句
@Fetch(FetchMode.SUBSELECT) 默认懒加载(除非设定关联属性lazy=false),在访问第一个关联对象时加载所有的关联对象。会累计产生两条sql语句。且FetchType设定有效
lazy代表延时加载,lazy=false,代表不延时,如果对象A中还有对象B的引用,会在A的xml映射文件中配置b的对象引用,多对一或一对多,不延时代表查询出对象A的时候,会把B对象也查询出来放到A对象的引用中,A对象中的B对象是有值的.如果lazy=true,代表延时,查询A对象时,不会把B对象也查询出来,只会在用到A对象中B对象时才会去查询,默认好像是false,你可以看看后台的sql语句的变化就明白了,一般需要优化效率的时候会用到.
双向一对多关系,一是关系维护端(owner side),多是关系被维护端(inverse side)。在关系被维护端需要通过@JoinColumn建立外键列指向关系维护端的主键列。
}
hibernate之实体@onetomany和@manytoone双向注解
下面是User类:
@onetomany
@Entity@Table(name="user")
public class User implements Serializable{
private static final long serialVersionUID = 6177417450707400228L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="id")
private int id;
@Column(name="name")
private String name;
@Column(name="password")
private String password;
/*
* cascade:为级联操作,里面有级联保存,级联删除等,all为所有
* fetch:加载类型,有lazy和eager二种,
* eager为急加载,意为立即加载,在类加载时就加载,lazy为慢加载,第一次调用的时候再加载,由于数据量太大,onetomany一般为lazy
* mappedBy:这个为manytoone中的对象名,这个不要变哦
* Set<role>:这个类型有两种,一种为list另一种为set
*
*
*/
@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY,mappedBy="user")
private Set<role> setRole;
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 getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<role> getSetRole() {
return setRole;
}
public void setSetRole(Set<role> setRole) {
this.setRole = setRole;
}
}
</role></role></role></role>
@manytoone
@Entity
@Table(name="role")
public class Role {
private static final long serialVersionUID = 6177417450707400228L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="role_id")
private int role_id;
@Column(name="role_name")
private String role_name;
@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
@JoinColumn(name="user_id")//加入一列作为外键
private User user;
public int getRole_id() {
return role_id;
}
public void setRole_id(int role_id) {
this.role_id = role_id;
}
public String getRole_name() {
return role_name;
}
public void setRole_name(String role_name) {
this.role_name = role_name;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
有了这二个注解,我们不需要建表,hibernate会为我们自动建立表以及各种表级联系
其中@table为映射表名,@column为映射列名,其他的我不一一细说了。
下面就是操作了,定义了级联操作,下面是保存的。
User user=new User();
user.setName("123456");
user.setPassword("123456");
Role role=new Role();
role.setRole_name("student");
role.setUser(user);
Role roler=registerDao.save(role);
return roler;
}
使用HIBERNATE的注解@ManyToOne(fetch = FetchType.lazy) 时候,报出错误
- org.hibernate.LazyInitializationException: could not initialize proxy - no Session
- at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:167)
- at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)
- at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
- at com.evolution.warlock.entity.Department_
_javassist_1.getName(Department__javassist_1.getName(Department__javassist_1.java)
- at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doSingleRealmAuthentication(ModularRealmAuthenticator.java:180)
- at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doAuthenticate(ModularRealmAuthenticator.java:267)
- at org.apache.shiro.authc.AbstractAuthenticator.authenticate(AbstractAuthenticator.java:198)
- fetch=FetchType.LAZY就等于打开了延迟加载,Hibernate中的get方法不论是否开启延迟加载都会一次加载所有基本数据类型属性的值,而load则不同,开启延迟加载后load(id),返回的对象会被Hibernate加一个壳(持久化上下文中没有缓存该对象的前提下),拦截所有非id属性的访问操作(getter,setter调用),只有id属性中有实际值(其实就是你调用load方法时传的那个),在session没有关闭的之前,如果访问除id外的其他属性才会发sql语句去查询,你这个错误就是因为在当前session关闭以后访问由load()加载的对象的非id属性,此时Hibernate尝试通过当前session发sql查询,但发现session已经关闭,这样才会报错。
我测试发现的确是上面所说的,LAZY阻挡了除ID以外的所有访问,所以解决的办法就很多,
一.就是只拿ID,再从数据库从新拿一次你需要的东西。
二.将FetchType.lazy 改成 EAGER
三.配置xml (没试过)
- <filter>
- <filter-name>openSessionInView</filter-name>
- <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>openSessionInView</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
四.在service层使用Hibernate.initialize()将你需要的对象从新持久化
五.在MVC里面配置,个人觉得这个办法更好
一、一对多(@OneToMany)
1、单向一对多模型
假设通过一个客户实体可以获得多个地址信息。
对于一对多的实体关系而言,表结构有两种设计策略,分别是外键关联和表关联。
(1) 映射策略---外键关联
在数据库中表customer和表结构address定义,如下:
- <code class="hljs sql" style=""><span class="hljs-keyword" style="">create</span> <span class="hljs-keyword" style="">table</span> customer (
- <span class="hljs-keyword" style="">id</span> <span class="hljs-built_in" style="">int</span>(<span class="hljs-number" style="">20</span>) <span class="hljs-keyword" style="">not</span> <span class="hljs-literal" style="">null</span> auto_increment,
- <span class="hljs-keyword" style="">name</span> <span class="hljs-built_in" style="">varchar</span>(<span class="hljs-number" style="">100</span>),
- primary <span class="hljs-keyword" style="">key</span>(<span class="hljs-keyword" style="">id</span>)
- )
- <span class="hljs-keyword" style="">create</span> <span class="hljs-keyword" style="">table</span> address (
- <span class="hljs-keyword" style="">id</span> <span class="hljs-built_in" style="">int</span>(<span class="hljs-number" style="">20</span>) <span class="hljs-keyword" style="">not</span> <span class="hljs-literal" style="">null</span> auto_increment,
- province <span class="hljs-built_in" style="">varchar</span>(<span class="hljs-number" style="">50</span>),
- city <span class="hljs-built_in" style="">varchar</span>(<span class="hljs-number" style="">50</span>),
- postcode <span class="hljs-built_in" style="">varchar</span>(<span class="hljs-number" style="">50</span>),
- detail <span class="hljs-built_in" style="">varchar</span>(<span class="hljs-number" style="">50</span>),
- customer_id <span class="hljs-built_in" style="">int</span>(<span class="hljs-number" style="">20</span>),
- primary <span class="hljs-keyword" style="">key</span> (<span class="hljs-keyword" style="">id</span>)
- )</code>
注意此时外键定义在多的一方,也就是address表中。
此时,表customer映射为实体CustomerEO,代码如下:
- <code class="hljs less" style=""><span class="hljs-variable" style="">@Entity</span>
- <span class="hljs-variable" style="">@Table</span>(name=<span class="hljs-string" style="">"customer"</span>)
- public class CustomerEO implements java.io.Serializable {
- <span class="hljs-variable" style="">@OneToMany</span>(cascade={ CascadeType.ALL })
- <span class="hljs-variable" style="">@JoinColumn</span>(name=<span class="hljs-string" style="">"customer_id"</span>)
- private Collection<AddressEO> addresses = new ArrayList<AddressEO>();
- ...
- }</code>
- <code class="hljs java" style=""><span class="hljs-meta" style="">@Target</span>({METHOD, FIELD}) <span class="hljs-meta" style="">@Retention</span>(RUNTIME)
- <span class="hljs-keyword" style="">public</span> <span class="hljs-meta" style="">@interface</span> OneToMany {
- <span class="hljs-function" style=""><span class="hljs-function" style="">Class </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">targetEntity</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">()</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">default</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style="">.class</span></span>;
- CascadeType[] cascade() <span class="hljs-keyword" style="">default</span> {};
- <span class="hljs-function" style=""><span class="hljs-function" style="">FetchType </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">fetch</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">()</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">default</span></span></span><span class="hljs-function" style=""> LAZY</span></span>;
- <span class="hljs-function" style=""><span class="hljs-function" style="">String </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">mappedBy</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">()</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">default</span></span></span><span class="hljs-function" style=""> ""</span></span>;
- }</code>
a、targetEntity属性表示默认关联的实体类型。如果集合类中指定了具体类型了,不需要使用targetEntity.否则要指定targetEntity=AddressEO.class。
b、mappedBy属性用于标记当实体之间是双向时使用。
(2) 映射策略---表关联
在上面address表中去掉customer_id字段,在增加一个表ref_customer_address,如下:
- <code class="hljs sql" style=""><span class="hljs-comment" style="">--客户地址关系表</span>
- <span class="hljs-keyword" style="">create</span> <span class="hljs-keyword" style="">table</span> ref_customer_address (
- customer_id <span class="hljs-built_in" style="">int</span>(<span class="hljs-number" style="">20</span>) <span class="hljs-keyword" style="">not</span> <span class="hljs-literal" style="">null</span>,
- address_id <span class="hljs-built_in" style="">int</span>(<span class="hljs-number" style="">20</span>) <span class="hljs-keyword" style="">not</span> <span class="hljs-literal" style="">null</span> <span class="hljs-keyword" style="">unique</span>
- )</code>
- <code class="hljs css" style="">@<span class="hljs-keyword" style="">Entity</span>
- @Table(name = <span class="hljs-string" style="">"customer"</span>)
- public class CustomerEO implements java.io.Serializable {
- ...
- @<span class="hljs-keyword" style="">OneToMany</span>(<span class="hljs-keyword" style="">cascade</span> = { <span class="hljs-selector-tag" style="">CascadeType</span><span class="hljs-selector-class" style="">.ALL</span> })
- @<span class="hljs-keyword" style="">JoinTable</span>(<span class="hljs-keyword" style="">name</span>="<span class="hljs-keyword" style="">ref_customer_address</span>",
- joinColumns={ @<span class="hljs-keyword" style="">JoinColumn</span>(<span class="hljs-keyword" style="">name</span>="<span class="hljs-keyword" style="">customer_id</span>",<span class="hljs-keyword" style="">referencedColumnName</span>="<span class="hljs-keyword" style="">id</span>")},
- inverseJoinColumns={@<span class="hljs-keyword" style="">JoinColumn</span>(<span class="hljs-keyword" style="">name</span>="<span class="hljs-keyword" style="">address_id</span>",<span class="hljs-keyword" style="">referencedColumnName</span>="<span class="hljs-keyword" style="">id</span>")})
- private Collection<AddressEO> addresses = new ArrayList<AddressEO>();
- ...
- }</code>
- <code class="hljs less" style=""><span class="hljs-variable" style="">@Target</span>({METHOD,FIELD})
- public <span class="hljs-variable" style="">@interface</span> JoinTable {
- String name() default "";
- String catalog() default "";
- String schema() default "";
- <span class="hljs-selector-tag" style="">JoinColumn</span><span class="hljs-selector-attr" style="">[]</span> <span class="hljs-selector-tag" style="">joinColumns</span>() <span class="hljs-selector-tag" style="">default</span> {};
- <span class="hljs-selector-tag" style="">JoinColumn</span><span class="hljs-selector-attr" style="">[]</span> <span class="hljs-selector-tag" style="">inverseJoinColumns</span>() <span class="hljs-selector-tag" style="">default</span> {};
- <span class="hljs-selector-tag" style="">UniqueConstraint</span><span class="hljs-selector-attr" style="">[]</span> <span class="hljs-selector-tag" style="">uniqueConstraints</span> <span class="hljs-selector-tag" style="">default</span> {};
- }</code>
其中:
a、该标记和@Table相似,用于标注用于关联的表。
b、name属性为连接两张表的表名。默认的表名为:“表名1”+“-”+“表名2”,上面例子默认的表名为customer_address。
c、joinColumns属性表示,在保存关系中的表中,所保存关联的外键字段。
d、inverseJoinColumns属性与joinColumns属性类似,不过它保存的是保存关系的另一个外键字段。
(3) 默认关联
在数据库底层为两张表添加约束,如下:
- <code class="hljs sql" style=""><span class="hljs-keyword" style="">create</span> <span class="hljs-keyword" style="">table</span> customer_address (
- customer_id <span class="hljs-built_in" style="">int</span>(<span class="hljs-number" style="">20</span>) <span class="hljs-keyword" style="">not</span> <span class="hljs-literal" style="">null</span>,
- address_id <span class="hljs-built_in" style="">int</span>(<span class="hljs-number" style="">20</span>) <span class="hljs-keyword" style="">not</span> <span class="hljs-literal" style="">null</span> <span class="hljs-keyword" style="">unique</span>
- )
- <span class="hljs-keyword" style="">alter</span> <span class="hljs-keyword" style="">table</span> customer_address <span class="hljs-keyword" style="">add</span> <span class="hljs-keyword" style="">constraint</span> fk_ref_customer foreign <span class="hljs-keyword" style="">key</span> (customer_id) <span class="hljs-keyword" style="">references</span> customer (<span class="hljs-keyword" style="">id</span>);
- <span class="hljs-keyword" style="">alter</span> <span class="hljs-keyword" style="">table</span> customer_address <span class="hljs-keyword" style="">add</span> <span class="hljs-keyword" style="">constraint</span> fk_ref_address foreign <span class="hljs-keyword" style="">key</span> (address_id) <span class="hljs-keyword" style="">references</span> address (<span class="hljs-keyword" style="">id</span>);</code>
这样,在CustomerEO中只需要在标注@OneToMany即可!
二、多对一@ManyToOne
1、单向多对一模型。
(1) 外键关联
配置AddressEO实体如下:
- <code class="hljs less" style=""><span class="hljs-variable" style="">@Entity</span>
- <span class="hljs-variable" style="">@Table</span>(name=<span class="hljs-string" style="">"address"</span>)
- public class AddressEO implements java.io.Serializable {
- <span class="hljs-variable" style="">@ManyToOne</span>(cascade = { CascadeType.ALL })
- <span class="hljs-variable" style="">@JoinColumn</span>(name=<span class="hljs-string" style="">"customer_id"</span>)
- private CustomerEO customer;
- <span class="hljs-comment" style="">// ...</span>
- }</code>
@ManyToOne定义如下:
- <code class="hljs java" style=""><span class="hljs-meta" style="">@Target</span>({METHOD,FIELD}) <span class="hljs-meta" style="">@Retention</span>(RUNTIME)
- <span class="hljs-keyword" style="">public</span> <span class="hljs-meta" style="">@interface</span> ManyToOne {
- <span class="hljs-function" style=""><span class="hljs-function" style="">Class </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">targetEntity</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">()</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">default</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style="">.class</span></span>;
- CascadeType[] cascade() <span class="hljs-keyword" style="">default</span> {};
- <span class="hljs-function" style=""><span class="hljs-function" style="">FetchType </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">fatch</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">()</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">default</span></span></span><span class="hljs-function" style=""> EAGER</span></span>;
- <span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">boolean</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">optional</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">()</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">default</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">true</span></span></span></span>;
- }</code>
(2) 默认关联
数据库脚本定义的相关字段的约束,创建外键后,直接使用@ManyToOne
三、高级一对多和多对一映射
即双向关联模型,确定了双向关联后,多的一方AddressEO不变使用@ManyToOne,而CustomerEO实体修改为:
- <code class="hljs less" style=""><span class="hljs-variable" style="">@Entity</span>
- <span class="hljs-variable" style="">@Table</span>(name=<span class="hljs-string" style="">"customer"</span>)
- public class CustomerEO {
- <span class="hljs-variable" style="">@OneToMany</span>(mappedBy=<span class="hljs-string" style="">"customer"</span>)
- private Collection<AddressEO> addresses = new ArrayList<AddressEO>();
- <span class="hljs-comment" style="">// ...</span>
- }</code>
四、多对多(@ManyToMany)
和一对多类型,不在赘述。@ManyToMany标记的定义如下:
- <code class="hljs java" style=""><span class="hljs-meta" style="">@Target</span>({METHOD, FIELD}) <span class="hljs-meta" style="">@Retention</span>(RUNTIME)
- <span class="hljs-keyword" style="">public</span> <span class="hljs-meta" style="">@interface</span> ManyToMany {
- <span class="hljs-function" style=""><span class="hljs-function" style="">Class </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">targetEntity</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">()</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">default</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style="">.class</span></span>;
- CascadeType[] cascade() <span class="hljs-keyword" style="">default</span> {};
- <span class="hljs-function" style=""><span class="hljs-function" style="">FetchType </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">fecth</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">()</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">default</span></span></span><span class="hljs-function" style=""> LAZY</span></span>;
- <span class="hljs-function" style=""><span class="hljs-function" style="">String </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">mappedBy</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">()</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">default</span></span></span><span class="hljs-function" style=""> ""</span></span>;
- }</code>
五、最后,谈谈关于集合类的选择
在映射关系中可以使用的集合类有Collection、Set、List和Map,下面看下如何选择。
1、定义时使用接口,初始化使用具体的类。
如Collection可以初始化为ArrayList或HashSet;
Set可以初始化为HashSet;
List可以初始化为ArrayList;
Map可以初始化为HashMap.
2、集合类的选择
Collection类是Set和List的父类,在未确定使用Set或List时可使用;
Set集合中对象不能重复,并且是无序的;
List集合中的对象可以有重复,并且可以有排序;
Map集合是带有key和value值的集合。