一、第一个应用实例
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进行配置的,我们以后称这种文件为实体配置文件(或是持久化类映射文件)
●
它可以有一个column属性用来指定表中的主键。同时注意在此标签下有一个
●
签没有指定column属性,即是说它会把name所关联的属性名作为字段名。如果不想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
name="hibernate.dialect">org.hibernate.dialect.MySQLDialect
主配置文件,完成了驱动注册,数据库连接,并关联了相应的java对象配置文件。
说明:
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.控制台显示:
三、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)取值与实体配置文件中
久化对象)的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类,而在实体配置文件中
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">
>>修改主配置文件,增加以下内容以便找到相应的实体配置文件。
>>步骤四,编写测试类。类中省略导入包的内容。
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 >>步骤二,修改Department的实体配置文件。在class元素下增加如下内容: 说明: >>步骤三,修改主配置文件,关联上面的实体配置文件。 >>步骤四,编写测试类,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.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”方式,其实这种方式也就指明了主键同时为外键。下面的指定了外键的参考信息,此元素中的内容指明了它参考 >>步骤三,修改主配置文件,关联上面的实体配置文件。 >>步骤四,编写测试类,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配置文件即可,修改后的内容如下: 说明:由于采取了外键关联,所以这里的从表的主键将不再作为外键参考主表的主键,所以它会采取一般的方式生成主键,即 双向关联,而这里如果注释掉此句,正好成了单向关联的一个例证。 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 } Teacher类内容如下:省略getXXX()、setXXX()方法。 package com.asm.hibernate.domain; import java.util.Set; public class Teacher { private int id; private String name; private Set } >>步骤二、为两个实体创建配置文件(省略了前面的xml文档声明内容): Student.hbm.xml内容如下: package="com.asm.hibernate.domain"> 说明:这里重点说明最后的 Teacher.hbm.xml内容如下: package="com.asm.hibernate.domain"> 由于和Student.hbm.xml是类似的,这里不作说明,这样也就建立起了双向的多对多关联。要注意他们所依赖的中间表为teacher_student,所以这里的 >>步骤四、编写测试文件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.add(t1); ts.add(t2); Set 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