《Hibernate入门笔记(43页).doc》由会员分享,可在线阅读,更多相关《Hibernate入门笔记(43页).doc(43页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、-Hibernate入门笔记-第 43 页一 、第一个应用实例1搭建环境:新建一个名为HibernateDemo的java工程,并导入Hibernate的jar包,特别要注意除了导入lib下的jar包还需导入hibernate3.jar核心jar包。 由于涉及数据库操作,还应导入mysql驱动包。说明,如果使用最新的hibernate,hibernate开发的基本jar包(7个) 来源:hibernate-distribution-.GA.zip hibernate3.jar librequired下的所有jar包2简述Hibernate的作用:ORM:Object Relational Ma
2、pping,对象关系映射。将java程序中的对象自动持久化到关系数据库中。而Hibernate的作用好比就是在java对象与关系数据库之间的一座桥梁,它主要负责两者之间的映射。在Hibernate内部封装了JDBC技术(但只是一个轻量级的封装,因而可以让程序设计人员更方便的以面向对象的思想操纵数据库),并向外提供API接口。3建新一个名为User.java的类,即是上面所说的java对象。我们以后称这种类为实体类(或是持久化类),它的对象为实体对象(或是持久化对象)。User.java内容如下:packageimportpublic class User private int id;priv
3、ate 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) this.name = name;public Date getDate() return date;public void setDate(Date date) this.date = date;4它和User.java放在同一个包下。内容如下:!DOCT
4、YPE hibernate-mapping PUBLIC -/Hibernate/Hibernate Mapping DTD 3.0/EN此配置文件,是用来为User.java进行配置的,我们以后称这种文件为实体配置文件(或是持久化类映射文件) 用来关联一个java类,注意在前面的根元素下有一个package属性,这样结合这个package和class标签下所指定的类名,就共同关联映射了一个java类。 其实可以这样理解,每一个包下都有实体配置文件,而这个配置文件开始的根元素package指定了此文件所处的位置(或是说它所关联的包),根元素下可以有多个标签(查阅dtd文件),它们可以分别来关联
5、包下的java类文件。 标签,一般建议至少有两个属性:name属性用来关联一个java类,比如这里关联了User类;table属性用来指定这个类所对应的表文件,如果不指定,系统会自动name指定的类文件进行关联(比如上面实际是:) 标签下的子标签:l 子标签实际就是用来映射主键,下的name就是用来指java类中的id属性,而它可以有一个column属性用来指定表中的主键。同时注意在此标签下有一个标签,它是用来指定主键的生成方式。 l 子标签,就是用来指定java类的属性映射到表中的一个字段,默认下此标签没有指定column属性,即是说它会把name所关联的属性名作为字段名。 如果不想java
6、类中的某些属性映射到表中,只要不用这个标签来关联这些属性即可。l 总结:上面的的name属性都分别指定了java类,java类的属性。而table,column是用来指定表,字段名配置文件!DOCTYPE hibernate-configuration PUBLIC-/Hibernate/Hibernate Configuration DTD 3.0/ENjdbc:mysql:/localhost:3306/testroot123456create主配置文件,完成了驱动注册,数据库连接,并关联了相应的java对象配置文件。说明:具体指定了关联的所有实体配置文件,关于它的作用可以注释掉此属性看效
7、果。另通过create指定了根据实体配置文件来自动生成表,其中包括:create/create-drop/update/validate四种可选方式。5编写测试类:UserTest.java 内容如下:packageimportimportimportimportimportimportpublic class UserTest public static void main(String args)Configuration cf=new Configuration();cf.configure();SessionFactory sf=cf.buildSessionFactory();Ses
8、sion s=sf.openSession();Transaction ts=s.beginTransaction(); /事务User user=new User();user.setName(jack);user.setDate(new Date();s.save(user);mit(); /提交事务s.close();System.out.println(done);6分析流程:首先抛开Transaction tx=s.beginTransaction()和mit(),因为它们是提交事务得。支持提交事务意味着支持数据回滚。说明,通常情况下,很多数据库都默认支持提交事务,所以加这两句代码非
9、常必要。 下面具体谈流程:第一步:获取SessionFactory对象,它会首先构建一个Configuration对象,此对象调用可以调用configure()和configure(String resource)这两种方法:这两种方法在Configuration中的源代码如下:public Configuration configure() throws HibernateException configure( );return this;public Configuration configure(String resource) throws HibernateException lo
10、g.info( configuring from resource: + resource );InputStream stream = getConfigurationInputStream( resource );return doConfigure( stream, resource );分析这两个源代码可以知道:无参调用最终也是调用这个有参数的方法,所以我们也可以直接传参数调用。 现在的重点是读配置文件,这个配置文件我们一般放在eclipse的scr根目录下,而当eclipse编译时会自动把这个目录下的文件编译到bin目录下,而这个bin目录下是被配置成classpath环境变量,而c
11、onfigure方法就是在classpath环境变量下查找配置文件。 再来分析,无参调用configure方法时,默认的是传递的配置文件,所以只有取名为这个的配置文件,才可以调用无参的configure方法,如果是其它名字的配置文件,则调用含参的配置文件,并且这个参数名应为这个配置文件的名字。 当读取配置文件后的Configuration对象,才是一个真正意义上可操控的实例对象。 然后,再用这个对象来构建一个SessionFactory对象。 强调说明,这一步整个操作最好是放在类的静态代码块中,因为它只在该类被加载时执行一次。第二步:得到一个Session实例,以进行数据库CRUD操作第三步:
12、实例化一个java类第四步:持久化操作第五步:后续操作:主要是关闭连接 7.实体类定义规则:Domain object(java对象)必须要有构造方法,同时建议有一个id属性,为了赖加载,这个java类的声明最好不用final。8.开发流程:官方推荐:先Domain object 再mapping,最后是DB。 常用开发方式:DB开始,由工具来生成mapping和Domain object。9.总结基本步骤:环境搭建(导入相关包等) 实体类及配置文件主配置文件(完成了数据库的配置及通过设置属性创建了相应的表)得到Session测试应用。二、 优化代码1为会么要优化在前面我们已经知道,获取Ses
13、sionFactory对象是一个重复的过程。因此我们可以把这个操作写成一Util类。下面我们把这一步写成工具类HibernateUtil,内容如下:packageimportimportimportpublic class HibernateUtil private static SessionFactory sf;private HibernateUtil() static Configuration cf = new Configuration();cf.configure();sf = cf.buildSessionFactory();public static SessionFacto
14、ry getSessionFactory() return sf;public static Session getSession() return sf.openSession();2优化测试类下面复制UserTest.java代码改为UserTest2.java并进行修改 修改后的内容如下:packageimportimportimportimportimport;importpublic class UserTest2 static void addUser(User user) Session s = null;Transaction ts = null;try s = Hiberna
15、teUtil.getSession();ts = s.beginTransaction();s.save(user);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方法中其实
16、也可以不用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); * /load只是准备连接到数据库,当增加上面一句操作时
17、表示有真正的数据库操作,这时它才会去连接数据库 return user; finally if (s != null)s.close();以上的代码,实现了数据库的查询操作,这里的get()方法需要传递两个参数,理解传递的参数:由于Session可以管理多个数据库所对应的多个实体对象,如果只是传递id将不能正确定位表,因而必须传递这个实体对象 ,get方法才能去查找这个实体对象所对应的数据库中的表。 用这个方法得到User对象后,便可以用此对象的方法来得到相关属性(也就是数据库表中的字段) 4.load()方法,懒加载。它的特点是:只有实际操作才会被加载,且它是生成的这个User.java的子
18、类,可以从打印结果看出。也正因此,所以前面建议实例类不使用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 说明
19、:这两种方法的主要区别主要体现在未开启事务时。save方法如果是没开启事务,会执行相关sql语句,随后再回滚。而presist根本就不执行这些sql语句。2删除对象:delete3更新数据:update 说明,如果数据库中没有记录将会出现异常4查找数据:get,立刻访问数据库 load,返回的是代理,不会立即访问数据库。5选择操作:saveOrUpdate,merge,根据id和version的值来确定是save还是update。saveOrUpdate方法的主要作用:可以把瞬时对象或脱管对象转成持久对象,而不需要具体判断对象是处在瞬时态或是脱管态来选择save或update来让对象变成持久态
20、。只要调用此方法就能由id和version来灵活选择是保存或更新。而merge方法一个对象后,对象仍是脱管态。5持久对象:lock,把对象变成持久对象,但不会同步对象的状态。四 、对象三种状态1瞬时(transient):数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来的且与Session无关系的对象。2脱管-游离(detached):数据库中有数据与之对应,但当前没有Session与之关联:脱管对象状态发生改变,Hibernate不能检测到。3持久(persistent):数据库有数据与之对应,当前与Session有关联,并且相关联的Session没有关闭,事务
21、没有提交:持久对象状态发生改变时,在事务提交时会影响到数据库。理解:与Session是否关联,数据库是否有数据与之对应是判断三种对象状态的依据。比如,瞬时状态跟它们均无关;脱管,只是数据库有数据与之对应,失去了Session对它的管理;而持久与两者者有关。从过程中理解三种对象状态:结合前面的实例,当我们User user=new User()一个对象时,它表示创建一个瞬时对象,当调用save(user)方法时,这个对象成为持久对象,直到事务提交,数据库连接关闭。在这期间,如果我们user.setXXX()时,会对这个持久对象产生影响,最终它也会被提交到数据库,它的最终提交是在提交事务时。比如s
22、ave(user)方法后,跟user.setName(new name);和user.setPassword(new password);这两句,这样它会在提交时务时,采取对数据库的更新操作,也就是说数据库连接关闭后,数据库存的是“new name”和“new password” 而如果开启了“数据库语言显示”可以发现执行两次操作:一次是save方法的插入操作,一次是setXXX后提交事务时的更新作(特别说明,持久对象在发生改变时,比如setXXX方法改变对象内容时,会在最后,即提交事务时统一进行更新操作,而并非每一次改变就执行一次更新,这样可以保证与数据库的交互更高效合理)。当执行完save
23、方法后,我们关闭数据库连接时,这时的user对象就是脱管状态,因为它在数据库有数据与之对应 而脱管状态的最好例子是当我们用get方法得到一个对象并关闭连接时 补充说明:既然我们已经知道了持久对象可以被Hibernate检测到进行更新操作,那么update是否还有用了?有,比如脱管对象就可以调用update来更新数据库中的数据,而调用update()方法后的脱管对象又变成了持久对象。 下面是三种对象状态相互转换的图例transientpersistentdetachedsava()saveOrUpdate()newdelete()get()load()find()iterate()evict()
24、close()clear()update()saveOrUpdate()lock()garbge()再谈saveOrUpdate方法:此方法兼具了save和update两种方法。它根据传递的参数来选择执行其中的一种方法。如果参数对象是瞬时态,则执行save方法,而如果参数对象是脱管态,则执行update方法。最终都是把传递的参数对象转成持久态。 如何判断对象的状态?主要依据是看:实体对象id(或者version)取值与实体配置文件中元素的unsaved-value属性值的匹配情况。 只要满足下面的任一情况则可以说明对象处在瞬时态:情况一,实体对象(持久化对象)的id取值为null形式(比如in
25、t型为0,字串为null)。 情况二,实体对象(持久化对象)的id取值与预设的unsaved-value属性值不同。 情况三,实体对象(持久化对象)的具有的versionn属性,并且为null。 情况四,实体对象(持久化对象)的version取值与预设的unsaved-value属性值不同。五、 完善工具类及HQL QBC初步相关1无聊的讨论:在前面我们写了一个工具类:HibernateUtil。其实我们还可以把CRUD操作封装到这个工具类中,并把它们都做成静态的,这样这个工具类就可以直接调用了。但是这样的操作对查询数据可能不是很好,因为它的查询方式很多,除非我们一一考虑这些可能涉及到查询方式
26、,并能以重载的形式进行统一管理。 其实我也试想过把这此数据库操作方法进行二次封装,在工具类写成如下形式:public void operate(int i )if(i=1) 调用更新方法,执行查询操作if(i=2) 调用删除方法,执行查询操作if(i=3) 调用插入方法,执行查询操作if(i=4)查询?可指定一个惯用的查询方法,但返回值如何处理,所以建议不在此处写查询,可再写一个查询的统一操作方法来总括所有的查询方法2HQL的作用概述数据库的操作,难点主要集中在查询操作中,而HQL就是专门用来为查询服务的。3HQL应用的步骤:假定我们已有一个Session对象s步骤一,获得Query对象:Qu
27、ery query=s.createQuery(“HQL SELECT Sentence”);步骤二,为参数赋值:query.setXXX();步骤三,获得List对象:LIST list=query.list(); 说明,除了此方法外,Query接口还有一个常用的方法uniqueResult,如果明确查询的结果只有一个,便选择使用此方法。如果查询结果有多个使用此方法会报异常。步骤四,遍历查询结果:即遍历上面list对象。关于步骤二为参数赋值的问题:比如步骤一中的“HQL Sentence”内容为:from User u where u.name=? and u.password=? and
28、.,如果这里的?较少可以setXXX(0,”.”); setXXX(1,”.”); 但是如果?较多,就容易把这些设置写错,所以可以采取命令参数的方式来决定后面的setXXX的内容。 比如:from User u where u.name=:uname and u.password=:upass and . ,这样后面就可以写setXXX(“uname”,”.”);4一个细节问题:在前面我们的实体类为User类,而在实体配置文件中意为它所关联的表为user表(没有指定名称table),但如果是oracle数据库,我们知道它本身就有一张user表,这样就产生了冲突,如何解决这种冲突?一种方法是设定
29、table属性为新的名字(首选方法),另一种方法是加(数字1前的那个符号),即这样写,这样我们的表还是user表。同样如果属性名与数据库中的关键字产生这种冲突,也可以按此方法解决。5.分页技术:query.setFirstResult(200);query.setMaxReslut(10);这两句的意思是符合要求的语句有很多条,我们从第200条取,取出10条。我们知道每种数据库的分页语句是不同的,而Hibernate底层判断使用哪种分页语句就是参照前面配置文件的方言属性。6.QBC条件查询:与它相关的是Criteria Interface,Criterion Interface,Express
30、son Class。其实它的操作和HQL很相似。同样我们假定已有一个Session对象s.步骤一,获得Criteria对象:Criteria criteria = s.createCriteria(User.class);步骤二,封装查询条件为一个Criterion对象:Criterion cr = Expression.eq(name, new name); (说明Expression继续于类),所以也可以这样写:Criterion cr=Restrictions.eq(name,new name); Restrictions类中的封装查询条件的方法都有两个参数:前一个参数是指创建Crite
31、ria对象时所使用的参数的属性名,后一个是要与属性名比较的值。比如这里是指User类的name属性是否与“new name”相等步骤三,获得带查询条件的Criteria对象:criteria.add(cr); 执行此步才使这个对象具有一个条件限制的查询操作。步骤四,获得List对象以遍历:List clist = criteria.list(); 补充说明:也可以直接返回一个User对象:User user=(User) criteria.uniqueResult();特别说明:Criteria对象也具有分页的功能,方式是和上面Query一样 。六 、基本应用实例:Dao设计1总体设计:设计U
32、ser对象及相关实体配置文件,工具类(得到一个Session对象),UserDao接口(实现此接口即以操作数据库),编写主配置文件,编写测试类。2UserDao的设计,最初我想打算设计成通用Object的操作,后来发现它的Session对象操作都要传递一个对象,就设计成如下形式。内容如下:packageimportpublic interface UserDao public void saveUser(User user);public User queryById(int id);public User queryByName(String name);public void update
33、(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一个Use
34、r对象,并设定的id,然后再把这个对象传递给删除方法。 但需要特别注意new出的对象必须完成符合我们通过id查出的对象。3这个实例参照前面的相关,基本可以写出。以下几点需要注意:导包:Hibernate包,数据库包;改写配置文件;查询方法的设计;注意事务,特别是“增删改”要注意事务。七 、关联关系讨论1多对一关系映射:一个部门有可以有多个员工,而一个员工只属于一个部门。从员工角度看,很多员工会隶属一个部门。现以实例说明,实例概要:一个部门类,只有id和部门名称两个属性。有一个员工类,有id和员工名称及部门对象三个属性。操作步骤如下:步骤一,建立Depatment.java及实体配置文件:pac
35、kagedomain;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;-同包下的实体配置文件:!DOCTYPE hibernate-mapping PUBLIC -/Hibernate/Hibernate Mapping
36、 DTD 3.0/EN以上的操作,没的什么可多言的,和前面的配置是一样的形式。步骤二,Employee.java内容如下及实体配置文件packagepublic 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) this.n
37、ame = name;public Department getDepart() return depart;public void setDepart(Department depart) this.depart = depart;-同包下的实体配置文件:Employee.hbm.xml!DOCTYPE hibernate-mapping PUBLIC -/Hibernate/Hibernate Mapping DTD 3.0/EN先来说这个类文件,它的一个重要属性就是Department对象,这就是它所关联的一个外键,这里我们只必须记住一点,每个实体类对应于一张表,如果一张表想关联另一张表
38、,则只需要在这张表所对应的实体类中引入它想关联表的实体类对象。再进行简单的配置即可。再来看配置文件,这里主要看这个元素,它的name属性仍然是实体类中的属性。column为对应表的外键。可以留意下数据库表中的相关表。修改主配置文件,增加以下内容以便找到相应的实体配置文件。 步骤四,编写测试类。类中省略导入包的内容。packagepublic class ManyToOneTest public static void main(String args) add();static void add() Session s = null;Transaction tx = null;try Depa
39、rtment 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语句。会再增加一条更新操作。mit(); finally if (s != null)s.close();说明
40、:以前插入新的记录,都要传递一个实体对象,而这里没有这么做,原因是为了体现出一个插入记录的顺序问题,仔细观察add()方法,发现先保存的是Department对象,再保存的是Employee对象,原因是什么?这里略作说明,我们知道此项目设计的是让Employee表来关联Department表,而要想关联此表,必须保证此表已进行存储,这里暂时这样理解,也可以试试颠倒save方法的顺序,或是不保存depart来看看效果,这样也许有助理解。简要说明,以上提到的尝试性操作:颠倒顺序,可以通过,只是增加了一条更新操作;不保存dep则不能通过,原因是此实体对象没有保存;再测试,如果注释掉“depart.s
41、etDname(deptName);”后面按正常顺序执行,发现是能正确执行的。只是在数据库的employee表中depart_id字段为null,如果我们在前面设置中增加:not-null=true这个属性,再来执行将会报“不能为空的错误”。通过增加这个属性,可以让我们更好理解如何建立两个对象的关联。步骤五,再探查询方法。接上面,在测试类中增加一个查询方法,内容如下:static Employee query(int empId) Session s = null;try s = HibernateUtil.getSession();Employee emp = (Employee) s.get(Employee.class, empId);