当前位置:文档之家› Hibernate3入门笔记

Hibernate3入门笔记

一、第一个应用实例

1.搭建环境:新建一个名为HibernateDemo的java工程,并导入Hibernate的jar包,特别要注意除了导入lib下的jar包还需导入hibernate3.jar核心jar包。由于涉及数据库操作,还应导入mysql驱动包。

说明,如果使用最新的hibernate,hibernate开发的基本jar包(7个) 来源:hibernate-distribution-3.3.2.GA.zip

hibernate3.jar

lib\required下的所有jar包

2.简述Hibernate的作用:ORM:Object Relational Mapping,对象关系映射。将java 程序中的对象自动持久化到关系数据库中。而Hibernate的作用好比就是在java对象与关系数据库之间的一座桥梁,它主要负责两者之间的映射。在Hibernate内部封装了JDBC 技术(但只是一个轻量级的封装,因而可以让程序设计人员更方便的以面向对象的思想操纵数据库),并向外提供API接口。

3.建新一个名为User.java的类,即是上面所说的java对象。我们以后称这种类为实体类(或是持久化类),它的对象为实体对象(或是持久化对象)。User.java内容如下:package com.asm.hibernate.domain;

import java.util.Date;

public class User {

private int id;

private String name;

private Date date;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

https://www.doczj.com/doc/5c10820769.html, = name;

}

public Date getDate() {

return date;

}

public void setDate(Date date) {

this.date = date;

}

}

4.编写配置文件:User.hbm.xml。它和User.java放在同一个包下。内容如下:

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"https://www.doczj.com/doc/5c10820769.html,/hibernate-mapping-3.0.dtd">

package="com.asm.hibernate.domain">

此配置文件,是用来为User.java进行配置的,我们以后称这种文件为实体配置文件(或是持久化类映射文件)用来关联一个java类,注意在前面的根元素下有一个package属性,这样结合这个package和class标签下所指定的类名,就共同关联映射了一个java类。其实可以这样理解,每一个包下都有实体配置文件,而这个配置文件开始的根元素package指定了此文件所处的位置(或是说它所关联的包),根元素下可以有多个标签(查阅dtd文件),它们可以分别来关联包下的java类文件。

标签,一般建议至少有两个属性:name属性用来关联一个java类,比如这里关联了User类;table属性用来指定这个类所对应的表文件,如果不指定,系统会自动name 指定的类文件进行关联(比如上面实际是:

标签下的子标签:

子标签实际就是用来映射主键,下的name就是用来指java类中的id属性,而

它可以有一个column属性用来指定表中的主键。同时注意在此标签下有一个

标签,它是用来指定主键的生成方式。

子标签,就是用来指定java类的属性映射到表中的一个字段,默认下此标

签没有指定column属性,即是说它会把name所关联的属性名作为字段名。如果不想java类中的某些属性映射到表中,只要不用这个标签来关联这些属性即可。

●总结:上面的的name属性都分别指定了java类,java类的

属性。而table,column是用来指定表,字段名

配置文件:hibernate.cfg.xml。它放在当前的项目的根目录下。内容如下:

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"https://www.doczj.com/doc/5c10820769.html,/hibernate-configuration-3.0.dtd">

name="hibernate.connection.driver_class">com.mysql.jdbc.Driver

name="hibernate.connection.url">jdbc:mysql://localhost:3306/test root

123456

name="hibernate.dialect">org.hibernate.dialect.MySQLDialect create

主配置文件,完成了驱动注册,数据库连接,并关联了相应的java对象配置文件。

说明:具体指定了关联的所有实体配置文件,关于它的作用可以注释掉此属性看效果。另通过create指定了根据实体配置文件来自动生成表,其中包括:create/create-drop/update/validate四种可选方式。

5.编写测试类:UserTest.java 内容如下:

package com.asm.hibernate.test;

import java.util.Date;

import org.hibernate.Session;

import org.hibernate.SessionFactory;

import org.hibernate.Transaction;

import org.hibernate.cfg.Configuration;

import https://www.doczj.com/doc/5c10820769.html,er;

public class UserTest {

public static void main(String []args){

Configuration cf=new Configuration();

cf.configure();

SessionFactory sf=cf.buildSessionFactory();

Session s=sf.openSession();

Transaction ts=s.beginTransaction(); //事务

User user=new User();

user.setName("jack");

user.setDate(new Date());

s.save(user);

https://www.doczj.com/doc/5c10820769.html,mit(); //提交事务

s.close();

System.out.println("done");

}

}

6.分析流程:首先抛开Transaction tx=s.beginTransaction()和https://www.doczj.com/doc/5c10820769.html,mit(),因为它们是提交事务得。支持提交事务意味着支持数据回滚。说明,通常情况下,很多数据库都默认支持提交事务,所以加这两句代码非常必要。下面具体谈流程:

第一步:获取SessionFactory对象,它会首先构建一个Configuration对象,此对象调用可以调用configure()和configure(String resource)这两种方法:这两种方法在Configuration中的源代码如下:

public Configuration configure() throws HibernateException {

configure( "/hibernate.cfg.xml" );

return this;

}

public Configuration configure(String resource) throws HibernateException { https://www.doczj.com/doc/5c10820769.html,( "configuring from resource: " + resource );

InputStream stream = getConfigurationInputStream( resource );

return doConfigure( stream, resource );

}

分析这两个源代码可以知道:无参调用最终也是调用这个有参数的方法,所以我们也可以直接传参数调用。现在的重点是读配置文件,这个配置文件我们一般放在eclipse的scr 根目录下,而当eclipse编译时会自动把这个目录下的文件编译到bin目录下,而这个bin 目录下是被配置成classpath环境变量,而configure方法就是在classpath环境变量下查找配置文件。再来分析,无参调用configure方法时,默认的是传递的hibernate.cfg.xml 配置文件,所以只有取名为这个的配置文件,才可以调用无参的configure方法,如果是其它名字的配置文件,则调用含参的配置文件,并且这个参数名应为这个配置文件的名字。当读取配置文件后的Configuration对象,才是一个真正意义上可操控的实例对象。然后,再用这个对象来构建一个SessionFactory对象。强调说明,这一步整个操作最好是放在类的静态代码块中,因为它只在该类被加载时执行一次。

第二步:得到一个Session实例,以进行数据库CRUD操作

第三步:实例化一个java类

第四步:持久化操作

第五步:后续操作:主要是关闭连接

7.实体类定义规则:

Domain object(java对象)必须要有构造方法,同时建议有一个id属性,为了赖加载,这个java类的声明最好不用final。

8.开发流程:

官方推荐:先Domain object 再mapping,最后是DB。常用开发方式:DB开始,由工具来生成mapping和Domain object。

9.总结基本步骤:

环境搭建(导入相关包等) —>实体类及配置文件—>主配置文件(完成了数据库的配置及通过设置属性创建了相应的表)—>得到Session测试应用。

二、优化代码

1.为会么要优化

在前面我们已经知道,获取SessionFactory对象是一个重复的过程。因此我们可以把这个操作写成一Util类。下面我们把这一步写成工具类HibernateUtil,内容如下:package com.asm.hibernate.utils;

import org.hibernate.Session;

import org.hibernate.SessionFactory;

import org.hibernate.cfg.Configuration;

public class HibernateUtil {

private static SessionFactory sf;

private HibernateUtil() {

}

static {

Configuration cf = new Configuration();

cf.configure();

sf = cf.buildSessionFactory();

}

public static SessionFactory getSessionFactory() {

return sf;

}

public static Session getSession() {

return sf.openSession();

}

}

2.优化测试类

下面复制UserTest.java代码改为UserTest2.java并进行修改修改后的内容如下:package com.asm.hibernate.test;

import java.util.Date;

import org.hibernate.HibernateException;

import org.hibernate.Session;

import org.hibernate.Transaction;

import https://www.doczj.com/doc/5c10820769.html,er;

import com.asm.hibernate.utils.HibernateUtil;

public class UserTest2 {

static void addUser(User user) {

Session s = null;

Transaction ts = null;

try {

s = HibernateUtil.getSession();

ts = s.beginTransaction();

s.save(user);

https://www.doczj.com/doc/5c10820769.html,mit();

} catch (HibernateException e) {

if (ts != null)

ts.rollback();

throw e;

} finally {

if (s != null)

s.close();

}

}

public static void main(String[] args) {

User user = new User();

user.setName("richie");

user.setDate(new Date());

addUser(user);

}

}

说明,在addUser方法中其实也可以不用catch语句捕获。因为关键的关闭连接已在finally实现。上面的例子可以作为以后Hibenate操作的一个典型模板,只需要修改主方法中的内容即可。

3.get方法:可以在UserTest2.java中增加这个方法:

static User getUser(int id) {

Session s = null;

try {

s = HibernateUtil.getSession();

return (User) s.get(User.class, id);

/*

* User user=(User) s.load(User.class,id);

* System.out.println("----load----"+user);

* System.out.println(user.getName());

* //load只是准备连接到数据库,当增加上面一句操作时表示有真正的数据库操作,这时它才会去连接数据库 return user;

*/

} finally {

if (s != null)

s.close();

}

}

以上的代码,实现了数据库的查询操作,这里的get()方法需要传递两个参数,理解传递的参数:由于Session可以管理多个数据库所对应的多个实体对象,如果只是传递id将不能正确定位表,因而必须传递这个实体对象,get方法才能去查找这个实体对象所对应的数据库中的表。用这个方法得到User对象后,便可以用此对象的方法来得到相关属性(也就是数据库表中的字段)

4.load()方法,懒加载。

它的特点是:只有实际操作才会被加载,且它是生成的这个User.java的子类,可以从打印结果看出。也正因此,所以前面建议实例类不使用final。强调:如果是懒加载,即使数据库中查不到数据,上面的user对象永远不会为空,因为它的内部实现实际上是new 了一个User(子)类对象。

下面再在main方法中测试,增加语句如下:

User u = getUser(1);

System.out.println("id=" + u.getId() + "\t name=" + u.getName());

5.控制台显示:

true在总配置文件中增加这个属性将会在控制台显示数据库操作的“数据库语言”。称这个属性为数据库语言显示。

三、Session中的主要方法

1.保存数据:save,presist 说明:这两种方法的主要区别主要体现在未开启事务时。save方法如果是没开启事务,会执行相关sql语句,随后再回滚。而presist根本就不执行这些sql语句。

2.删除对象:delete

3.更新数据:update 说明,如果数据库中没有记录将会出现异常

4.查找数据:get,立刻访问数据库 load,返回的是代理,不会立即访问数据库。5.选择操作:saveOrUpdate,merge,根据id和version的值来确定是save还是update。saveOrUpdate方法的主要作用:可以把瞬时对象或脱管对象转成持久对象,而不需要具体判断对象是处在瞬时态或是脱管态来选择save或update来让对象变成持久态。只要调用此方法就能由id和version来灵活选择是保存或更新。而merge方法一个对象后,对象仍是脱管态。

5.持久对象:lock,把对象变成持久对象,但不会同步对象的状态。

四、对象三种状态

1.瞬时(transient):数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来的且与Session无关系的对象。

2.脱管-游离(detached):数据库中有数据与之对应,但当前没有Session与之关联:脱管对象状态发生改变,Hibernate不能检测到。

3.持久(persistent):数据库有数据与之对应,当前与Session有关联,并且相关联的Session没有关闭,事务没有提交:持久对象状态发生改变时,在事务提交时会影响到数据库。

理解:与Session是否关联,数据库是否有数据与之对应是判断三种对象状态的依据。比如,瞬时状态跟它们均无关;脱管,只是数据库有数据与之对应,失去了Session对它的管理;而持久与两者者有关。

从过程中理解三种对象状态:结合前面的实例,当我们User user=new User()一个对象时,它表示创建一个瞬时对象,当调用save(user)方法时,这个对象成为持久对象,直到事务提交,数据库连接关闭。在这期间,如果我们user.setXXX()时,会对这个持久对象产生影响,最终它也会被提交到数据库,它的最终提交是在提交事务时。比如save(user)方法后,跟user.setName("new name");和user.setPassword("new password");这两句,这样它会在提交时务时,采取对数据库的更新操作,也就是说数据库连接关闭后,数据库存的是“new name”和“new password”而如果开启了“数据库语言显示”可以发现执行两次操作:一次是save方法的插入操作,一次是setXXX后提交事务时的更新作(特别说明,持久对象在发生改变时,比如setXXX方法改变对象内容时,会在最后,即提交事务时统一进行更新操作,而并非每一次改变就执行一次更新,这样可以保证与数据库的交互更高效合理)。当执行完save方法后,我们关闭数据库连接时,这时的user对象就是脱管状态,因为它在数据库有数据与之对应而脱管状态的最好例子是当我们用get方法得到一个对

象并关闭连接时

补充说明:既然我们已经知道了持久对象可以被Hibernate检测到进行更新操作,那么update是否还有用了?有,比如脱管对象就可以调用update来更新数据库中的数据,而调用update()方法后的脱管对象又变成了持久对象。下面是三种对象状态相互转换的图例

再谈saveOrUpdate方法:此方法兼具了save和update两种方法。它根据传递的参数来选择执行其中的一种方法。如果参数对象是瞬时态,则执行save方法,而如果参数对象是脱管态,则执行update方法。最终都是把传递的参数对象转成持久态。如何判断对象的状态?主要依据是看:实体对象id(或者version)取值与实体配置文件中元素的unsaved-value属性值的匹配情况。只要满足下面的任一情况则可以说明对象处在瞬时态:情况一,实体对象(持久化对象)的id取值为null形式(比如int型为0,字串为null)。情况二,实体对象(持久化对象)的id取值与预设的unsaved-value属性值不同。情况三,实体对象(持久化对象)的具有的versionn属性,并且为null。情况四,实体对象(持

久化对象)的version取值与预设的unsaved-value属性值不同。

五、完善工具类及HQL QBC初步相关

1.无聊的讨论:

在前面我们写了一个工具类:HibernateUtil。其实我们还可以把CRUD操作封装到这个工具类中,并把它们都做成静态的,这样这个工具类就可以直接调用了。但是这样的操作对查询数据可能不是很好,因为它的查询方式很多,除非我们一一考虑这些可能涉及到查询方式,并能以重载的形式进行统一管理。其实我也试想过把这此数据库操作方法进行二次封装,在工具类写成如下形式:

public void operate(int i ){

if(i==1){ 调用更新方法,执行查询操作}

if(i==2){ 调用删除方法,执行查询操作}

if(i==3){ 调用插入方法,执行查询操作}

if(i==4){查询?可指定一个惯用的查询方法,但返回值如何处理,所以建议不在此处写查询,可再写一个查询的统一操作方法来总括所有的查询方法}

}

2.HQL的作用概述

数据库的操作,难点主要集中在查询操作中,而HQL就是专门用来为查询服务的。3.HQL应用的步骤:

假定我们已有一个Session对象s

>>步骤一,获得Query对象:Query query=s.createQuery(“HQL SELECT S entence”); >>步骤二,为参数赋值:query.setXXX();

>>步骤三,获得List对象:LIST list=query.list(); 说明,除了此方法外,Query接口还有一个常用的方法uniqueResult,如果明确查询的结果只有一个,便选择使用此方法。如果查询结果有多个使用此方法会报异常。

>>步骤四,遍历查询结果:即遍历上面list对象。

关于步骤二为参数赋值的问题:比如步骤一中的“HQL Sentence”内容为:from User u where https://www.doczj.com/doc/5c10820769.html,=? and u.password=? and ...,如果这里的?较少可以setXXX(0,”...”); setXXX(1,”...”); 但是如果?较多,就容易把这些设置写错,所以可以采取命令参数的方式来决定后面的setXXX的内容。比如:from User u where https://www.doczj.com/doc/5c10820769.html,=:uname and u.password=:upass and ... ,这样后面就可以写setXXX(“uname”,”...”);

4.一个细节问题:

在前面我们的实体类为User类,而在实体配置文件中意为它所关联的表为user表(没有指定名称table),但如果是oracle数据库,我们知道它本身就有一张user表,这样就产生了冲突,如何解决这种冲突?一种方法是设定table属性为新的名字(首选方法),另一种方法是加`(数字1前的那个符号),即这样写,这样我们的表还是user表。同样如果属性名与数据库中的关键字产生这种冲突,也可以按此方法解决。

5.分页技术:

query.setFirstResult(200);query.setMaxReslut(10);这两句的意思是符合要求的语句有很多条,我们从第200条取,取出10条。我们知道每种数据库的分页语句是不同的,而Hibernate底层判断使用哪种分页语句就是参照前面配置文件的方言属性。

6.QBC条件查询:

与它相关的是Criteria Interface,Criterion Interface,Expressson Class。其实它的操作和HQL很相似。同样我们假定已有一个Session对象s.

>>步骤一,获得Criteria对象:Criteria criteria = s.createCriteria(User.class); >>步骤二,封装查询条件为一个Criterion对象:Criterion cr = Expression.eq("name",

"new name"); (说明Expression继续于org.hibernate.criterion.Restrictions类),所以也可以这样写:Criterion cr=Restrictions.eq("name","new name"); Restrictions 类中的封装查询条件的方法都有两个参数:前一个参数是指创建Criteria对象时所使用的参数的属性名,后一个是要与属性名比较的值。比如这里是指User类的name属性是否与“new name”相等

>>步骤三,获得带查询条件的Criteria对象:criteria.add(cr); 执行此步才使这个对象具有一个条件限制的查询操作。

>>步骤四,获得List对象以遍历:List clist = criteria.list(); 补充说明:也可以直接返回一个User对象:User user=(User) criteria.uniqueResult();

特别说明:Criteria对象也具有分页的功能,方式是和上面Query一样。

六、基本应用实例:Dao设计

1.总体设计:设计User对象及相关实体配置文件,工具类(得到一个Session对象),UserDao接口(实现此接口即以操作数据库),编写主配置文件,编写测试类。2.UserDao的设计,最初我想打算设计成通用Object的操作,后来发现它的Session对象操作都要传递一个对象,就设计成如下形式。内容如下:

package com.asm.dao;

import https://www.doczj.com/doc/5c10820769.html,er;

public interface UserDao {

public void saveUser(User user);

public User queryById(int id);

public User queryByName(String name);

public void update(User user);

public void delete(User user);

}

按此设计,意思是此类专门针对User对象的数据库操作,传递User对象,所以后面它的实现类的query相关方法可以直接user = (User) s.get(User.class, name);写name为传递的参数,而我们知道操作的是User对象,所以直接可以User.class。值得一提的是,在JDBC操作中,delete,传递id这种值就可以实现删除,而Hibernate的删除操作,必须传递一个对象,操作过程就是我们通过id查出这个对象,再把这个对象传递给删除方法以供删除。而实事上也可以new一个User对象,并设定的id,然后再把这个对象传递给删除方法。但需要特别注意new出的对象必须完成符合我们通过id查出的对象。3.这个实例参照前面的相关,基本可以写出。以下几点需要注意:导包:Hibernate包,数据库包;改写配置文件;查询方法的设计;注意事务,特别是“增删改”要注意事务。

七、关联关系讨论

1.多对一关系映射:

一个部门有可以有多个员工,而一个员工只属于一个部门。从员工角度看,很多员工会隶属一个部门。现以实例说明,实例概要:一个部门类,只有id和部门名称两个属性。有一个员工类,有id和员工名称及部门对象三个属性。操作步骤如下:

>>步骤一,建立Depatment.java及实体配置文件:

package com.asm.hibernate.domain;

public class Department {

private int id ;

private String name;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

https://www.doczj.com/doc/5c10820769.html, = name;

}

}

----同包下的实体配置文件:Depart.hbm.xml

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"https://www.doczj.com/doc/5c10820769.html,/hibernate-mapping-3.0.dtd">

以上的操作,没的什么可多言的,和前面的配置是一样的形式。

>>步骤二,Employee.java内容如下及实体配置文件

package com.asm.hibernate.domain;

public class Employee {

private int id;

private String name;

private Department depart;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

https://www.doczj.com/doc/5c10820769.html, = name;

}

public Department getDepart() {

return depart;

}

public void setDepart(Department depart) {

this.depart = depart;

}

}

----同包下的实体配置文件:Employee.hbm.xml

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"https://www.doczj.com/doc/5c10820769.html,/hibernate-mapping-3.0.dtd">

先来说这个类文件,它的一个重要属性就是Department对象,这就是它所关联的一个外键,这里我们只必须记住一点,每个实体类对应于一张表,如果一张表想关联另一张表,则只需要在这张表所对应的实体类中引入它想关联表的实体类对象。再进行简单的配置即可。再来看配置文件,这里主要看这个

column="depart_id">元素,它的name属性仍然是实体类中的属性。column 为对应表的外键。可以留意下数据库表中的相关表。

>>修改主配置文件,增加以下内容以便找到相应的实体配置文件。

>>步骤四,编写测试类。类中省略导入包的内容。

package com.asm.hibernate.test;

public class ManyToOneTest {

public static void main(String[] args) {

add();

}

static void add() {

Session s = null;

Transaction tx = null;

try {

Department depart = new Department();

depart.setName("departName");

Employee emp = new Employee();

emp.setName("empName");

emp.setDepart(depart);

s = HibernateUtil.getSession();

tx = s.beginTransaction();

s.save(depart);

s.save(emp);

// 交换以上两句的位置,看Hibernate执行的sql语句。会再增加一条更新操作。

https://www.doczj.com/doc/5c10820769.html,mit();

} finally {

if (s != null)

s.close();

}

}

}

说明:以前插入新的记录,都要传递一个实体对象,而这里没有这么做,原因是为了体现出一个插入记录的顺序问题,仔细观察add()方法,发现先保存的是Department对象,再保存的是Employee对象,原因是什么?这里略作说明,我们知道此项目设计的是让Employee表来关联Department表,而要想关联此表,必须保证此表已进行存储,这里暂时这样理解,也可以试试颠倒save方法的顺序,或是不保存depart来看看效果,这样也许有助理解。

简要说明,以上提到的尝试性操作:颠倒顺序,可以通过,只是增加了一条更新操作;不保存dep则不能通过,原因是此实体对象没有保存;再测试,如果注释掉

“depart.setDname("deptName");”后面按正常顺序执行,发现是能正确执行的。只是在数据库的employee表中depart_id字段为null,如果我们在前面设置

name="depart" column="depart_id">中增加:not-null="true"这个属性,再来执行将会报“不能为空的错误”。通过增加这个属性,可以让我们更好理解如何建立两个对象的关联。

>>步骤五,再探查询方法。接上面,在测试类中增加一个查询方法,内容如下:

static Employee query(int empId) {

Session s = null;

try {

s = HibernateUtil.getSession();

Employee emp = (Employee) s.get(Employee.class, empId);

System.out.println("Department Name:" + emp.getDepart().getName());

return emp;

} finally {

if (s != null)

s.close();

}

}

随后再在主方法中增加如下内容:便可以测试结果:

query(1);

对结果的说明分析:如果在以往的JDBC操作中,我们想得到这个部门的信息,需要查两次才能得到,这里的 Hibernate就实现了这一优化,如果我们开启了控制台的数据库显示功能(主配置文件中开启,前面已提到),便可以查看下数据库的执行操作,并切实感受到这里实际执行了两步查询操作,在以后的操作中,建议开启此属性,以便切实体会数据库操作。

>>步骤六,增加一个新的查询方法query2,内容如下,注意和query区别:static Employee query2(int empId) {

try {

s = HibernateUtil.getSession();

Employee emp = (Employee) s.get(Employee.class, empId);

Hibernate.initialize(emp.getDepart());

//上一句作用后面会作说明,这里略作了解

return emp;

} finally {

if (s != null)

s.close();

}

}

在main方法中增加如下内容进行测试:

Employee emp = query2(1);

System.out.println(emp.getDepart().getName());

2.一对多关系映射:

上面提到的多个员工对应于一个部门,是多对一的关系,如果一个部门有多个员工,则用“一对多”的关系来表示这个关系,现接上例继续,以实现一对多的关系映射。具体操作步骤如下:

>>步骤一,在Department.java中增加如下内容:private Set emps; 及相应的get/set方法。

>>步骤二,修改Department的实体配置文件。在class元素下增加如下内容:

说明: 元素同样是指定一个属性,只不过是指定一个特殊的属性,key中的column 为映射文件的外键,这里是指Employee.java相关的实体对象的外键。中的class表示关联的外键为Employee对象,也即Set中泛型机制指定的类。

>>步骤三,修改主配置文件,关联上面的实体配置文件。

>>步骤四,编写测试类,OneToManyTest.java 内容(省略导入的包)如下:

package com.asm.hibernate.test;

public class OneToManyTest {

public static void main(String[] args) {

add();

query(1);

}

static Department query(int departId) {

Session s = null;

try {

s = HibernateUtil.getSession();

Department depart = (Department) s.get(Department.class, departId);

System.out.println("employee size:" + depart.getEmps().size());

return depart;

} finally {

s.close();

}

}

static void add() {

Session s = null;

Transaction tx = null;

try {

Department depart = new Department();

depart.setName("departName");

Employee emp1 = new Employee();

emp1.setName("empName1");

emp1.setDepart(depart);

Employee emp2 = new Employee();

emp2.setName("empName2");

emp2.setDepart(depart);

// Set emps = new HashSet();

// emps.add(emp1);

// emps.add(emp2);

// depart.setEmps(emps);

s = HibernateUtil.getSession();

tx = s.beginTransaction();

s.save(depart);

s.save(emp1);

s.save(emp2);

https://www.doczj.com/doc/5c10820769.html,mit();

} finally {

if (s != null)

s.close();

}

}

}

说明:此类比较简单,增加两个员工信息,然后查出,同样可以体现出这种查询的方便,可以查看控制台,发现实质也是进行了两次查询操作。特别注意,上面注释掉的内容,思考怎样实现映射?如果加上注释掉的内容将会在控制台增加两条更新操作(注意更新的内容)。实际上注释掉的内容和已有内容建立了两种关联:“多对一”、“一对多”。

3.一对一关系映射:

典型的实例:一个人有一个身份证,而一个身份证只属于某一个人。以此为模型实现一对一关系的映射。下面的实例先以主键进行关联:

主键关联:从表的主键同时又作为外键参考主表的主键。比如在下面的实例中,人作为主表,身份证作为从表。

>>步骤一,建立Person类及相关的实体配置文件。

package com.asm.hibernate.domain;

public class Person {

private int id;

private String name;

private IdCard idCard;

} ...省略相应的get/set方法。

配置文件:person.hbm.xml 和前面的配置一样,只需要留意下元素,内容如下:

>>步骤二,建立IdCard类及相关的实体配置文件。

package com.asm.hibernate.domain;

import java.util.Date;

public class IdCard {

private int id;

private Date validity;

private Person person;

} ...省略相应的get/set方法。

从表配置文件:IdCard.hbm.xml ,内容如下:

person

配置文件说明:由于上面提到的是采取主键关联,即是说这里的id即是主键,同时也是(关联相关表的)外键,因此,以下对id的生成采取了”foreign”方式,其实这种方式也就指明了主键同时为外键。下面的指定了外键的参考信息,此元素中的内容指明了它参考的person。注意在中设定了constrained属性,其作用是说明该配置文件所映射表的主键同时作为外键,参照关联类对应表的主键。设定了此属性可用“show create table idcard”查看表idcard的定义发生了变化。

>>步骤三,修改主配置文件,关联上面的实体配置文件。

>>步骤四,编写测试类,OneToOneTest.java 内容如下:省略导入的包。

package com.asm.hibernate.test;

public class OneToOneTest {

public static void main(String[] args) {

add();

}

static void add() {

Session s = null;

Transaction tr = null;

try {

s = HibernateUtil.getSession();

tr = s.beginTransaction();

Person person = new Person();

person.setName("pName");

IdCard idCard = new IdCard();

idCard.setValidity(new Date());

//分别注释掉以下两句,看程序执行情况

person.setIdCard(idCard);

idCard.setPerson(person);

s.save(person);

s.save(idCard);

https://www.doczj.com/doc/5c10820769.html,mit();

} finally {

if (s != null)

s.close();

}

}

}

说明:留意上面的注释,如果注释掉第一句,发现一切正常,因为主对象是可以没有此属性,它的实体配置文件也基本与前面一样。而如果注释掉下面一句,将会报错,原因是“attempted to assign id from null one-to-one property: person”,IdCard的实体配置文件关联了一个表,而它采取主键关联,而主键关联要依赖于person属性的id,如果这里注释掉,即没有了此属性,它也关联不了相应的id。简单的说,IdCard来要关联Person,我们称它为从对象,而person并不关联谁,我们称为主对象。现在只要记住,从对象关联了表(关联了主对象),必须设定它所关联的主对象属性

>>步骤五,编写两个查询方法,一个查询主对象,主要代码:

static Person query(int id) {

Session s = null;

Transaction tr = null;

try {

s = HibernateUtil.getSession();

tr = s.beginTransaction();

Person p = (Person) s.get(Person.class, id);

System.out.println("身份证有效期:" + p.getIdCard().getValidity());

https://www.doczj.com/doc/5c10820769.html,mit();

return p;

} finally {

if (s != null)

s.close();

}

}

然后再在main方法中调用此方法,并开启控制台数据库库语言显示后,可以从控制台看出查询主对象只select一次;再增加一个查询从对象的方法,主要代码:

static IdCard query2(int id) {

Session s = null;

Transaction tr = null;

try {

s = HibernateUtil.getSession();

tr = s.beginTransaction();

IdCard idCard = (IdCard) s.get(IdCard.class, id);

//System.out.println("人的名字:" + idCard.getPerson().getName());

//去掉上一句注释后,发现会查询两次。

https://www.doczj.com/doc/5c10820769.html,mit();

return idCard;

} finally {

if (s != null)

s.close();

}

}

同样在main方法中调用此方法,并开启控制台数据库库语言显示后。从控制台看出也只会查询一次,但是如果去掉注释后发现会查询两次。接着,在此例的基础上修改成外键关联。

外键关联:从表的主键并不作为外键参考主表的主键,而是将其它字段作为外键参的主键。其实在上例的基础上,我们只需要修改IdCard.hbm.xml配置文件即可,修改后的内容如下:

说明:由于采取了外键关联,所以这里的从表的主键将不再作为外键参考主表的主键,所以它会采取一般的方式生成主键,即生成和以前的那此相同采取“native”方式。另注意到< many-to-one >,发现增加了unique有属性,这样尽管是多对一,但能有效保证实质是一对一。这时运行原OneToOneTest,发现仍是和以前一样。如果我们再修改Person的实体配置文件如下:

特别要注意到property-ref属性。可以结合执行后表的结构来看。其实如果注释掉此句,其结果就是通过身份证可以查看到Person的相关信息,但是通过Person却不能找到身份证信息,因为Hibernate完全依赖实体配置文件(映射文件)。注释掉当然就不能找到。而事实上这时在OneToOne中调用query方法,会发现出现空指针异常。其实在前面的关联关系中,最终都是实现了

双向关联,而这里如果注释掉此句,正好成了单向关联的一个例证。

4.多对多关系映射:

典型实例:一个学生可以有多个老师,同样一个老师可以有多个学生,对此设计如下:

师关联,通过此表进行学生和老师之间的交互查找。

teacher表查找到id,再到teacher_student表中以teacher.id==teacher_student.teacher_id为查询条件来查找studnent_id ,查找到studnet_id后,再以teacher_student.stucent_id==student.id为查询条件来查找所有学生的信息。同样通过学生查老师,也是类似的过程。

>>步骤一、创建实体类Student、Teacher

Student类内容如下:省略getXXX()、setXXX()方法。

package com.asm.hibernate.domain;

import java.util.Set;

public class Student {

private int id;

private String name;

private Set teachers;

}

Teacher类内容如下:省略getXXX()、setXXX()方法。

package com.asm.hibernate.domain;

import java.util.Set;

public class Teacher {

private int id;

private String name;

private Set students;

}

>>步骤二、为两个实体创建配置文件(省略了前面的xml文档声明内容):

Student.hbm.xml内容如下:

package="com.asm.hibernate.domain">

说明:这里重点说明最后的配置:(1)Student中Set类型的属性配置一个元素,其实在前面的一对多中已经出现了元素的配置,我们可以这样认为“凡是要为某个Set类型的属性进行映射配置,都可以用元素来配置”。 中的table属性为关联表的名称。(2)它的子元素中的column为关联表中以该映射文件所映射的表的主键为外键的字段名称. (3) coumn属性为关联表中以欲关联类对应表的主键为外键的字段名称。

Teacher.hbm.xml内容如下:

package="com.asm.hibernate.domain">

由于和Student.hbm.xml是类似的,这里不作说明,这样也就建立起了双向的多对多关联。要注意他们所依赖的中间表为teacher_student,所以这里的元素中的table属性和teacher映射文件相同,而特别要注意中column中属性值的设定。>>步骤三、在主配置文件中关联实体配置文件:只需要增加如下内容:

>>步骤四、编写测试文件ManyToManyTest.java:省略导入的包。

package com.asm.hibernate.test;

public class ManyToManyTest {

public static void main(String[] args) {

add();

}

static void add() {

Session s = null;

Transaction tr = null;

try {

s = HibernateUtil.getSession();

tr = s.beginTransaction();

Teacher t1 = new Teacher();

t1.setName("t1Name");

Teacher t2 = new Teacher();

t2.setName("t2Name");

Student s1 = new Student();

s1.setName("s1Name");

Student s2 = new Student();

s2.setName("s2Name");

// 再增加如下内容进行测试:

Set ts = new HashSet();

ts.add(t1);

ts.add(t2);

Set ss = new HashSet();

ss.add(s1);

ss.add(s2);

t1.setStudents(ss);

t2.setStudents(ss);

// s1.setTeachers(ts);

// s2.setTeachers(ts);

// 增加内容完

s.save(s1);

s.save(s2);

s.save(t1);

s.save(t2);

https://www.doczj.com/doc/5c10820769.html,mit();

} finally {

if (s != null)

s.close();

}

}

}

说明:注意以上注释掉的内容,如果去掉会出现异常。理解:加上增加的内容后再执行发现,在开启“数据库显示”功能后,发现控制台中新增加了四条插入语句,且是插入到中间表teacher_student中,在此处相当于告诉了每个学生所关联到的老师,而如果在mysql 客户端执行“show create table teacher_student”,观察它的表结构并结合“去掉注释的报错说明”,就容易理解到为什么不能有注释掉的内容。另需要说明的是“多对多”在操作和性能方面都不太理想,所以它使用较少,一般我们会选择转换成“一对多”的模型,而Hiberante的“多对多”实现,可能也是转换成两个“一对多”来实现。

5.组件关系映射:

典型实例:每个人有不同的名字,或者多个人可以是同一个名字,对于这种现象可以是“一个一”或者“多对一”,这样会被设计成两张表来映射,而我们采用组件关系来映射就可以在一张表中映射实现。具体过程如下:

>>步骤一、编写实体文件Name.java 和People.java

相关主题
文本预览
相关文档 最新文档