[键入文字]
4.load()方法,懒加载。
它的特点是:只有实际操作才会被加载,且它是生成的这个User.java的子类,可以从打印结果看出。也正因此,所以前面建议实例类不使用final。强调:如果是懒加载,即使数据库中查不到数据,上面的user对象永远不会为空,因为它的内部实现实际上是new了一个User(子)类对象。
下面再在main方法中测试,增加语句如下: User u = getUser(1);
System.out.println(\ + u.getId() + \ + 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(\和user.setPassword(\这两句,这样它会在提交时务时,采取对数据库的更新操作,也就是说数据库连接关闭后,数据库存的是“new name”和“new password”。 而如果开启了“数据库语言显示”可以发现执行两次操作:一次是save方法的插入操作,一次是setXXX后提交事务时的更新作(特别说明,持久对象在发生改变时,比如setXXX方法改变对象内容时,会在最后,即提交事务时统一进行更新操作,而并非每一次改变就执行一次更新,这样可以保证与数据库的交互更高效合理)。当执行完save方法后,我们关闭数据库连接时,这时的user对象就是脱管状态,因为它在数据库有数据与之对应, 而脱管状态的最好例子是当我们用get方法得到一个对象并关闭连接时
补充说明:
既然我们已经知道了持久对象可以被Hibernate检测到进行更新操作,那么update是否还有用了?有,比如脱管对象就可以调用update来更新数据库中的数据,而调用update()方法后的脱管对象又变成了持久对象。
下面是三种对象状态相互转换的图例
- 6 - / 42
[键入文字]
new get() load() find() iterate() transient sava() saveOrUpdate() delete() garbge() persistent evict() close() clear() detached update() saveOrUpdate() lock()
再谈saveOrUpdate方法:此方法兼具了save和update两种方法。它根据传递的参数来选择执行其中的一种方法。
如果参数对象是瞬时态,则执行save方法,而如果参数对象是脱管态,则执行update方法,
最终都是把传递的参数对象转成持久态。
如何判断对象的状态?
主要依据是看:实体对象id(或者version)取值与实体配置文件中
只要满足下面的任一情况则可以说明对象处在瞬时态:
情况一,实体对象(持久化对象)的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 Sentence”); >>步骤二:为参数赋值:query.setXXX();
>>步骤三:获得List对象:LIST list=query.list(); 说明,除了此方法外,Query接口还有一个常用的方法uniqueResult,如果明确查询的结果只有一个,便选择使用此方法。如果查询结果有多个使用此方法会报异常。
- 7 - / 42
[键入文字]
>>步骤四:遍历查询结果:即遍历上面list对象。
关于步骤二为参数赋值的问题:比如步骤一中的“HQL Sentence”内容为:from User u where u.name=?
and u.password=? and ...,如果这里的?较少可以setXXX(0,”...”); setXXX(1,”...”); 但是如果?较多,就容易把这些设置写错,所以可以采取命令参数的方式来决定后面的setXXX的内容。 比如:from User u where u.name=: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(\(说明Expression继续于org.hibernate.criterion.Restrictions类),所以也可以这样写:Criterion cr=Restrictions.eq(\类中的封装查询条件的方法都有两个参数:前一个参数是指创建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 com.asm.domain.User; 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 - 8 - / 42 [键入文字] 的删除操作,必须传递一个对象,操作过程就是我们通过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) { this.name = name; } } ----同包下的实体配置文件:Depart.hbm.xml \ \ 以上的操作,没的什么可多言的,和前面的配置是一样的形式。 >>步骤二,Employee.java内容如下及实体配置文件 package com.asm.hibernate.domain; public class Employee { private int id; private String name; - 9 - / 42 [键入文字] } 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) { this.name = name; } public Department getDepart() { return depart; } public void setDepart(Department depart) { this.depart = depart; } ----同包下的实体配置文件:Employee.hbm.xml \ \ 先来说这个类文件,它的一个重要属性就是Department对象,这就是它所关联的一个外键,这里我们只必须记住一点,每个实体类对应于一张表,如果一张表想关联另一张表,则只需要在这张表所对应的实体类中引入它想关联表的实体类对象。再进行简单的配置即可。再来看配置文件,这里主要看这个 package com.asm.hibernate.test; public class ManyToOneTest { public static void main(String[] args) { } static void add() { add(); - 10 - / 42