这个星期在看《Hibernate开发指南》,边看边动手做了小例子,先记录下来。
Hibernate的关联关系映射主要有三种:一对一关联一对多关联多对多关联
一、使用*.hbm.xml配置文件做简单示例
一对一关联包括两种形式:主键关联、唯一外键关联主键关联:两张关联表通过主键形成一对一的映射关系。Hibernate中通过one-to-one节点对一对一关系进行定义。示例:一个用户User属于一个组GroupUser实体类:
public class User { private Integer id; private String name; private Group group; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Group getGroup() { return group; } public void setGroup(Group group) { this.group = group; } }
Group实体类:
public class Group { private Integer id; private String name; private User user; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
User.hbm.xml
<hibernate-mapping> <class name="com.petrochina.User" table="cui_user" dynamic-update="true" dynamic-insert="true"> <id name="id" type="java.lang.Integer"> <generator class="native" /> </id> <property name="name" type="java.lang.String"/> <one-to-one name="group" class="com.petrochina.Group" cascade="all" outer-join="auto"></one-to-one> </class> </hibernate-mapping>
Group.hbm.xml
<hibernate-mapping> <class name="com.petrochina.Group" table="cui_group" > <id name="id" type="java.lang.Integer"> <generator class="foreign" > //foreign主键生成 <param name="property">user</param> </generator> </id> <property name="name" type="java.lang.String"/> <one-to-one name="user" class="com.petrochina.User"></one-to-one> </class> </hibernate-mapping>
测试方法:
public void oneToOne() { try { User user=new User(); user.setName("two"); Group group=new Group(); group.setName("测试"); group.setUser(user); user.setGroup(group); Transaction tx=session.beginTransaction(); session.save(user); tx.commit(); System.out.println(user.getName()); } catch (Exception e) { e.printStackTrace(); } }
唯一外键关联:通过增加外键字段实现,这种方式是多对一关系的一个特例。User类没有变化,Group类中去掉user字段:
public class Group { private Integer id; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } }
User.hbm.xml
<hibernate-mapping> <class name="com.petrochina.User" table="one_user" dynamic-update="true" dynamic-insert="true"> <id name="id" type="java.lang.Integer"> <generator class="native" /> </id> <property name="name" type="java.lang.String"/> <many-to-one name="group" column="group_id" class="com.petrochina.Group" cascade="all" /> //定义group_id字段 </class> </hibernate-mapping>
Group.hbm.xml
<hibernate-mapping> <class name="com.petrochina.Group" table="cui_group" > <id name="id" type="java.lang.Integer"> <generator class="assigned" > </generator> </id> <property name="name" type="java.lang.String"/> </class> </hibernate-mapping>
测试方法:
public void oneToOne() { try { Transaction tx=session.beginTransaction(); User user=new User(); user.setName("three"); Group group=new Group(); group.setId(999); group.setName("测试3"); user.setGroup(group); session.save(user); tx.commit(); System.out.println(user.getName()); } catch (Exception e) { e.printStackTrace(); } }
注意:hibernate.cfg.xml的映射文件中不能存在同名文件,即使路径不一样。
二、使用JPA实现*.hbm.xml相同的功能首先,使用JPA需要更改HibernateSessionFactory中Configuration的实现:private static Configuration configuration = new
AnnotationConfiguration(); 或者在自定义Configuration cfg=new AnnotationConfiguration().configure();其次,去掉hibernate.cfg.xml中<mapping resource="com/petrochina/onetoone/User.hbm.xml" />部分对映射文件的配置,改为<mapping class="com.petrochina.jpa.User" />1、一对一
一对一关系有三种情况
示例:用户User、组Group
(1)关联的实体都共享同样的主键
1)一对一双向关联
@Entity @Table(name="t_user") public class User { private Integer id; private String name; private Group group; @Id @GeneratedValue(strategy=GenerationType.AUTO) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne(cascade=CascadeType.ALL) @PrimaryKeyJoinColumn public Group getGroup() { return group; } public void setGroup(Group group) { this.group = group; } } @Entity @Table(name="t_group") public class Group { private Integer id; private String name; private User user; @Id @GenericGenerator(name="pkGenerator",strategy="foreign",parameters={@Parameter(value="user", name = "property")}) @GeneratedValue(generator="pkGenerator")//根据User的id填充Group的id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne(cascade=CascadeType.ALL,mappedBy="group") public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
执行插入测试方法:
public void oneToOne() { try { Transaction tx=session.beginTransaction(); User user=new User(); user.setName("csdner"); Group group=new Group(); group.setName("developer"); user.setGroup(group); group.setUser(user); session.save(user); tx.commit(); } catch (Exception e) { e.printStackTrace(); } }
Hibernate生成的sql语句如下:
Hibernate: select hibernate_sequence.nextval from dualHibernate: insert into t_user (name, id) values (?, ?)Hibernate: insert into t_group (name, id) values (?, ?)
查询表里的数据
SQL> select * from t_user; ID NAME -------------------------------------------------------------------------------- 276 csdner
SQL> select * from t_group; ID NAME -------------------------------------------------------------------------------- 276 developer
可见,User和Group有相同的ID。
执行查询测试方法:
public void getUser() { User user=(User)session.load(User.class, 276); System.out.println(user.getName()); System.out.println(user.getGroup().getName()); }
Hibernate生成的sql语句如下:
select user0_.id as id2_2_, user0_.name as name2_2_, group1_.id as id3_0_, group1_.name as name3_0_, user2_.id as id2_1_, user2_.name as name2_1_ from t_user user0_ left outer join t_group group1_ on user0_.id = group1_.id left outer join t_user user2_ on group1_.id = user2_.id where user0_.id = ? csdner
developer
可见User和Group通过ID关联。
2)一对一单向关联
@Entity @Table(name="t_user") public class User { private Integer id; private String name; @Id @GeneratedValue(strategy=GenerationType.AUTO) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } @Entity @Table(name="t_group") public class Group { private Integer id; private String name; private User user; @Id @GenericGenerator(name="pkGenerator",strategy="foreign",parameters={@Parameter(value="user", name = "property")}) @GeneratedValue(generator="pkGenerator") public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne(cascade=CascadeType.ALL) @PrimaryKeyJoinColumn public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
执行插入测试方法:
public void oneToOne() { try { Transaction tx=session.beginTransaction(); User user=new User(); user.setName("csdner"); Group group=new Group(); group.setName("developer"); group.setUser(user); session.save(group);//保存group,同时级联保存user tx.commit(); } catch (Exception e) { e.printStackTrace(); } }
Hibernate生成的sql语句如下:
Hibernate: select hibernate_sequence.nextval from dualHibernate: insert into t_user (name, id) values (?, ?)Hibernate: insert into t_group (name, id) values (?, ?)
查询表里的数据:
SQL> select * from t_user;
ID NAME---------- -------------------- 280 csdner
SQL> select * from t_group;
ID NAME---------- -------------------- 280 developer
User对象和Group对象也有相同的ID,但是由于是单向的,只能通过Group对象查找对应的User对象。
(2)其中一个实体通过外键关联到另一个实体的主键。注:一对一,则外键必须为唯一约束
@Entity @Table(name="t_user") public class User { private Integer id; private String name; private Group group; @Id @GeneratedValue(strategy=GenerationType.AUTO) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne(cascade=CascadeType.ALL) @JoinColumn(name="gp_id")//定义关联列的列名,默认为“group_id” public Group getGroup() { return group; } public void setGroup(Group group) { this.group = group; } } @Entity @Table(name="t_group") public class Group { private Integer id; private String name; private User user; @Id @GeneratedValue //@GenericGenerator(name="pkGenerator",strategy="foreign",parameters={@Parameter(value="user", name = "property")}) //@GeneratedValue(generator="pkGenerator") public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne(cascade=CascadeType.ALL,mappedBy="group") public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
通过@JoinColumn注解定义一对一的关联关系。如果没有@JoinColumn注解,则系统自动处理,在主表中将创建连接列,列名为:主题的关联属性名 + 下划线 + 被关联端的主键列名。
执行插入测试代码:
public void oneToOne() { try { Transaction tx=session.beginTransaction(); User user=new User(); user.setName("csdner"); Group group=new Group(); group.setName("developer"); user.setGroup(group); group.setUser(user); session.save(user); tx.commit(); } catch (Exception e) { e.printStackTrace(); } }
Hibernate生成的sql语句如下:
Hibernate: select hibernate_sequence.nextval from dualHibernate: select hibernate_sequence.nextval from dualHibernate: insert into t_group (name, id) values (?, ?)Hibernate: insert into t_user (gp_id, name, id) values (?, ?, ?)
查询表里的数据:
SQL> select * from t_user;
ID NAME GP_ID---------- -------------------- ---------- 287 csdner 288
SQL> select * from t_group;
ID NAME---------- -------------------- 288 developer
User对象通过group_id 列关联Group对象
(3)通过关联表来保存两个实体之间的关联关系。注:一对一,则关联表每个外键都必须是唯一约束
@Entity @Table(name="t_user") public class User { private Integer id; private String name; private Group group; @Id @GeneratedValue(strategy=GenerationType.AUTO) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne(cascade=CascadeType.ALL) @JoinTable(name="t_user_group",joinColumns={@JoinColumn(name="user_id",referencedColumnName="id")},inverseJoinColumns={@JoinColumn(name="group_id",referencedColumnName="id")})//指定关联表t_user_group,其中与User关联的列为user_id,与Group关联的列为group_id public Group getGroup() { return group; } public void setGroup(Group group) { this.group = group; } } @Entity @Table(name="t_group") public class Group { private Integer id; private String name; //private User user;//不能含有user,否则会报错,后续再看看 @Id @GeneratedValue public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } //@OneToOne(mappedBy="group") //public User getUser() { // return user; //} //public void setUser(User user) { // this.user = user; //} }
执行插入测试方法:
public void oneToOne() { try { Transaction tx=session.beginTransaction(); User user=new User(); user.setName("csdner"); Group group=new Group(); group.setName("developer"); user.setGroup(group); session.save(user); tx.commit(); } catch (Exception e) { e.printStackTrace(); } }
Hibernate生成的sql语句如下:
Hibernate: select hibernate_sequence.nextval from dualHibernate: select hibernate_sequence.nextval from dualHibernate: insert into t_group (name, id) values (?, ?)Hibernate: insert into t_user (name, id) values (?, ?)Hibernate: insert into t_user_group (group_id, user_id) values (?, ?)
查询表里的数据:
SQL> select * from t_user;
ID NAME---------- -------------------- 313 csdner
SQL> select * from t_group;
ID NAME---------- -------------------- 314 developer
SQL> select * from t_user_group;
USER_ID GROUP_ID---------- ---------- 313 314
2、一对多
一对多关联包括两种形式:单向一对多关联、双向一对多关联单向一对多关联只需在“一”方进行配置(onetomany),即通过“一”关联到“多”。双向一对多需要关联双方均加以配置,这样既能通过“一”关联其对应的“多”,也可以通过“多”关联其对应的“一”。以用户TUser和地址TAddress做演示
单向一对多:只在TUser类中维护@OneToMany
//用户类TUser.java @Entity @Table(name="cui_user") public class TUser { private Integer id; private String name; private List<TAddress> addresses=new ArrayList<TAddress>();//注意一定要实例化 @Id//标识主键 @GeneratedValue(strategy=GenerationType.AUTO)//指定主键值的产生策略由Hibernate根据数据库字段选择 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL)//FetchType.LAZY延迟加载,CascadeType.ALL所有操作都同步 @JoinColumn(name="user_id",referencedColumnName="id")//在TAddress对应的表中增加关联列user_id,其值对应TUser的id字段 @OrderBy(value="id asc")//所去地址列表按id升序排列 public List<TAddress> getAddresses() { return addresses; } public void setAddresses(List<TAddress> addresses) { this.addresses = addresses; } } //地址类TAddress.java @Entity @Table(name="cui_addr") public class TAddress { private Integer id; private String zipCode; private String addr; @Id @GeneratedValue(strategy=GenerationType.AUTO) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name="zip_code") public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } public String getAddr() { return addr; } public void setAddr(String addr) { this.addr = addr; } }
测试方法:
public void oneToMany() { try { Transaction tx=session.beginTransaction(); TUser user=new TUser(); user.setName("test"); TAddress addr1=new TAddress(); addr1.setZipCode("027"); addr1.setAddr("武汉"); TAddress addr2=new TAddress(); addr2.setZipCode("021"); addr2.setAddr("上海"); user.getAddresses().add(addr1); user.getAddresses().add(addr2); session.save(user); tx.commit(); } catch (Exception e) { e.printStackTrace(); } }
对应的Hibernate生成的sql语句如下:Hibernate: insert into cui_user (name, id) values (?, ?)Hibernate: insert into cui_addr (addr, zip_code, id) values (?, ?, ?)Hibernate: insert into cui_addr (addr, zip_code, id) values (?, ?, ?)Hibernate: update cui_addr set user_id=? where id=?Hibernate: update cui_addr set user_id=? where id=?可见单向一对多中Hibernate是先插入TAddress记录,然后再维护关联关系字段user_id.
双向一对多:在TUser类中维护@OneToMany,在TAddress类中维护@ManyToOne
//用户类TUser.java @Entity @Table(name="cui_user") public class TUser { private Integer id; private String name; private List<TAddress> addresses=new ArrayList<TAddress>();//注意一定要实例化 @Id //标识主键 @GeneratedValue(strategy=GenerationType.AUTO)//指定主键值的产生策略由Hibernate根据数据库字段选择 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } //fetch=FetchType.LAZY延迟加载,cascade=CascadeType.ALL所有操作都同步 @OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="user") @OrderBy(value="id asc")//所去地址列表按id升序排列 public List<TAddress> getAddresses() { return addresses; } public void setAddresses(List<TAddress> addresses) { this.addresses = addresses; } } //地址类TAddress.java @Entity @Table(name="cui_addr") public class TAddress { private Integer id; private String zipCode; private String addr; private TUser user; @Id @GeneratedValue(strategy=GenerationType.AUTO) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name="zip_code") public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } public String getAddr() { return addr; } public void setAddr(String addr) { this.addr = addr; } @ManyToOne @JoinColumn(name="user_id",referencedColumnName="id")//在TAddress对应的表中增加关联列user_id,其值对应TUser的id字段 public TUser getUser() { return user; } public void setUser(TUser user) { this.user = user; } }
测试代码:
public void oneToMany() { try { Transaction tx=session.beginTransaction(); TUser user=new TUser(); user.setName("test"); TAddress addr1=new TAddress(); addr1.setZipCode("027"); addr1.setAddr("武汉"); TAddress addr2=new TAddress(); addr2.setZipCode("021"); addr2.setAddr("上海"); addr1.setUser(user); addr2.setUser(user); user.getAddresses().add(addr1); user.getAddresses().add(addr2); session.save(user); tx.commit(); System.out.println(user.getName()); } catch (Exception e) { e.printStackTrace(); } }
对应的Hibernate生成的sql语句如下:Hibernate: insert into cui_user (name, id) values (?, ?)Hibernate: insert into cui_addr (addr, user_id, zip_code, id) values (?, ?, ?, ?)Hibernate: insert into cui_addr (addr, user_id, zip_code, id) values (?, ?, ?, ?)可见在双向一对多关联中在新增TAddress记录时直接将关联字段user_id填充了,相比单向一对多少了两条更新语句。
3、多对多
示例:角色Role、权限Privilege
@Entity @Table(name="t_role") public class Role { private Integer id; private String name; private Set<Privilege> privileges=new HashSet<Privilege>(); @Id @GeneratedValue(strategy=GenerationType.AUTO) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @ManyToMany(fetch=FetchType.LAZY) @JoinTable(name="t_role_privilege",joinColumns={@JoinColumn(name="role_id",referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="privilege_id",referencedColumnName="id")}) public Set<Privilege> getPrivileges() { return privileges; } public void setPrivileges(Set<Privilege> privileges) { this.privileges = privileges; } } @Entity @Table(name="t_privilege") public class Privilege { private Integer id; private String name; private Set<Role> roles=new HashSet<Role>(); @Id @GeneratedValue(strategy=GenerationType.AUTO) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @ManyToMany(mappedBy="privileges") public Set<Role> getRoles() { return roles; } public void setRoles(Set<Role> roles) { this.roles = roles; } }
测试方法:
public void manyToMany() { Role role1=new Role(); role1.setName("Role1"); Role role2=new Role(); role2.setName("Role2"); Privilege privilege1=new Privilege(); privilege1.setName("Privilege1"); Privilege privilege2=new Privilege(); privilege2.setName("Privilege2"); privilege1.getRoles().add(role1); privilege1.getRoles().add(role2); privilege2.getRoles().add(role1); role1.getPrivileges().add(privilege1); role1.getPrivileges().add(privilege2); role2.getPrivileges().add(privilege1); Transaction tx=session.beginTransaction(); //多对多关系必须同时对关联双方进行保存 session.save(privilege1); session.save(privilege2); session.save(role1); session.save(role2); tx.commit(); }
对应的Hibernate生成的sql语句如下:Hibernate: insert into t_privilege (name, id) values (?, ?)Hibernate: insert into t_privilege (name, id) values (?, ?)Hibernate: insert into t_role (name, id) values (?, ?)Hibernate: insert into t_role (name, id) values (?, ?)Hibernate: insert into t_role_privilege (role_id, privilege_id) values (?, ?)Hibernate: insert into t_role_privilege (role_id, privilege_id) values (?, ?)Hibernate: insert into t_role_privilege (role_id, privilege_id) values (?, ?)
