Hibernate学习笔记(1)Hibernate构造
一 准备工作
首先,我们将创建一个简单的基于控制台(console-based)Hibernate应用。
我们所做的第一件事就是创建我们的开发文件夹。并把所有需要用到的Java件放进去。解压缩从Hibernate站点下载的Hibernate公布包。并把全部须要的库文件拷到我们项目中去。学习建User-library-hibernate,并增加对应的jar包
(a)项目右键-buildpath-configure build path-add library
(c)在该library中增加hibernate所需jar包
到编写本文时为止。这些是Hibernate执行所须要的最小库文件集合(注意我们也拷贝了 Hibernate3.jar,这个是最基本的文件)。
你正使用的Hibernate版本号可能须要比这很多其它或少一些的库文件。请參见公布包中的lib/文件夹下的README.txt,以获取很多其它关于所需和可选的第三方库文件信息(其实。Log4j并非必须的库文件,但被很多开发人员所喜欢)。
(d) 引入sqlserver的JDBC驱动包
在sqlserver中创建表StudentInfo
接下来我们创建一个类。用来代表那些我们希望储存在数据库里的student。
二 持久化类
我们的第一个持久化类是一个带有一些属性(property)的简单JavaBean类:
package com.model; public class StudentInfo { private int id; private String name; private int age; private String sex; //构造方法 public StudentInfo(){ } 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 int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } }
你能够看到这个类对属性的存取方法(getter and setter method)使用了标准JavaBean命名约定。同一时候把类属性(field)的訪问级别设成私有的(private)。
这是推荐的设计,但并非必须的。Hibernate也能够直接訪问这些field,而使用訪问方法(accessor method)的优点是提供了重构时的健壮性(robustness)。
为了通过反射机制(Reflection)来实例化这个类的对象。我们须要提供一个无參的构造器(no-argument constructor)。
对一特定的StudentInfo, id 属性持有唯一的标识符(identifier)的值。假设我们希望使用Hibernate提供的全部特性,
那么全部的持久化实体(persistent entity)类(这里也包含一些次要依赖类)都须要一个这种标识符属性。
而其实,大多数应用程序(特别是web应用程序)都须要通过标识符来差别对象,所以你应该考虑使用标识符属性而不是把它当作一种限制。
然而,我们通常不会操作对象的标识(identity),因此它的setter方法的訪问级别应该声明private。
这样当对象被保存的时候,仅仅有Hibernate能够为它分配标识符值。
你可看到Hibernate能够直接訪问public,private和protected的訪问方法和field。
所以选择哪种方式全然取决于你。你能够使你的选择与你的应用程序设计相吻合。
全部的持久化类(persistent classes)都要求有无參的构造器。由于Hibernate必须使用Java反射机制来为你创建对象。
构造器(constructor)的訪问级别能够是private,然而当生成执行时代理(runtime proxy)的时候则要求使用至少是package 级别的訪问控制,
这样在没有字节码指令(bytecode instrumentation)的情况下。从持久化类里获取数据会更有效率。
下一步,我们把这个持久化类的信息告诉Hibernate。
三 映射文件
Hibernate须要知道如何去载入(load)和存储(store)持久化类的对象。这正是Hibernate映射文件发挥作用的地方。
映射文件告诉Hibernate它,应该訪问数据库(database)里面的哪个表(table)及应该使用表里面的哪些字段(column)。
一个映射文件的基本结构看起来像这样:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> [...] </hibernate-mapping>
注意Hibernate的DTD是很复杂的。
你的编辑器或者IDE里使用它来自己主动完毕那些用来映射的XML元素(element)和属性(attribute)。
你也能够在文本编辑器里打开DTD-这是最简单的方式来概览全部的元素和attribute,并查看它们的缺省值以及凝视。
注意Hibernate不会从web载入DTD文件,
但它会首先在应用程序的classpath中查找。DTD文件已包含在hibernate3.jar里,同一时候也在Hibernate公布包的src/文件夹下。
为缩短代码长度,在以后的样例里我们会省略DTD的声明。当然。在实际的应用程序中,DTD声明是必须的。
在hibernate-mapping标签(tag)之间, 含有一个class元素。全部的持久化实体类(再次声明,也许接下来会有依赖类。就是那些次要的实体)都须要一个这种映射,来把类对象映射到SQL数据库里的表。
<hibernate-mapping> <class name="com.model.StudentInfo" table="StudentInfo"> </class> </hibernate-mapping>
到眼下为止,我们告诉了Hibernate如何把StudentInfo类的对象持久化到数据库的StudentInfo表里。以及如何从StudentInfo表载入到StudentInfo类的对象。
每一个实例相应着数据库表中的一行。如今我们将继续讨论有关唯一标识符属性到数据库表的映射。另外。因为我们不关心如何处理这个标识符。我们就配置由Hibernate的标识符生成策略来产生代理主键字段。
<hibernate-mapping> <class name="com.model.StudentInfo" table="StudentInfo"> <id name="id" column="ID"> <generator class="native"/> </id> </class> </hibernate-mapping>
id元素是标识符属性的声明,name="id" 声明了Java属性的名字 - Hibernate会使用getId()和setId()来訪问它。
column属性则告诉Hibernate, 我们使用StudentInfo表的哪个字段作为主键。
嵌套的generator元素指定了标识符生成策略,在这里我们指定native,
它依据已配置的数据库(方言)自己主动选择最佳的标识符生成策略。
Hibernate支持由数据库生成,全局唯一性(globally unique)和应用程序指定(或者你自己为不论什么已有策略所写的扩展)这些策略来生成标识符。
最后我们在映射文件中面包括须要持久化属性的声明。
默认情况下。类里面的属性都被视为非持久化的:
<hibernate-mapping> <class name="com.model.StudentInfo" table="StudentInfo"> <id name="id" column="ID"> <generator class="native"/> </id> <property name="date" type="timestamp" column="EnterDate"/> <property name="title"/> </class> </hibernate-mapping>
和id
元素一样。property
元素的name
属性告诉Hibernate使用哪个getter和setter方法。在此例中。Hibernate会寻找getDate()/setDate()
, 以及getTitle()/setTitle()
。
为什么date
属性的映射含有column
attribute,而title
却没有?当没有设定column
attribute 的时候。Hibernate缺省地使用JavaBean的属性名作为字段名。对于title
,这样工作得非常好。然而。date
在多数的数据库里。是一个保留keyword,所以我们最好把它映射成一个不同的名字。
还有一有趣的事情是title
属性缺少一个type
attribute。
我们在映射文件中声明并使用的类型。却不是我们期望的那样。是Java数据类型。同一时候也不是SQL数据库的数据类型。这些类型就是所谓的Hibernate 映射类型(mapping types),它们能把Java数据类型转换到SQL数据类型。反之亦然。再次重申。假设在映射文件中没有设置type
属性的话,Hibernate会自己试着去确定正确的转换类型和它的映射类型。在某些情况下这个自己主动检測机制(在Java
类上使用反射机制)不会产生你所期待或须要的缺省值。date
属性就是个非常好的样例,Hibernate无法知道这个属性(java.util.Date
类型的)应该被映射成:SQLdate
。或timestamp
,还是time
字段。
在此例中,把这个属性映射成timestamp
转换器。这样我们预留了日期和时间的所有信息。
应该把这个映射文件保存为Event.hbm.xml
,且就在StudentInfo Java类的源文件文件夹下。映射文件可任意地命名,但hbm.xml
的后缀已成为Hibernate开发人员社区的约定。
对于StudentInfo的配置例如以下:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.model.StudentInfo" table="StudentInfo"> <id name="id" column="ID"> <generator class="native"/> </id> <property name="name" column = "Name"/> <property name="age" column = "Age"/> <property name="sex" column = "Sex"/> </class> </hibernate-mapping>
我们继续进行Hibernate的主要配置。
四 Hibernate配置
如今我们已经有了一个持久化类和它的映射文件,该是配置Hibernate的时候了。在此之前。我们须要一个数据库。
Hibernate是你的应用程序里连接数据库的那层。所以它须要连接用的信息。连接(connection)是通过一个也由我们配置的JDBC连接池(connection pool)来完毕的。
Hibernate的公布包里包括了很多开源的(open source)连接池。注意,假设你希望使用一个产品级(production-quality)的第三方连接池软件,你必须拷贝所需的库文件到你的classpath下,并使用不同的连接池设置。
为了保存Hibernate的配置,我们能够使用一个简单的hibernate.properties文件,或者一个略微复杂的hibernate.cfg.xml,甚至能够全然使用程序来配置Hibernate。多数用户更喜欢使用XML配置文件:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- Database connection settings --> <!-- MySql配置 --> <!--<property name="connection.driver_class">com.mysql.jdbc.Driver</property> --> <!--<property name="connection.url">jdbc:mysql://localhost/hibernate</property> --> <!-- SQL dialect --> <!--<property name="dialect">org.hibernate.dialect.MySQLDialect</property>--> <!-- SqlServer配置 --> <property name="connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property> <property name="connection.url">jdbc:sqlserver://localhost/Hibernate</property> <!-- 数据库用户名 --> <property name="connection.username">sa</property> <!-- 数据库密码 --> <property name="connection.password">123</property> <!-- JDBC connection pool (use the built-in) --> <property name="connection.pool_size">1</property> <!-- SQL dialect --> <property name="dialect">org.hibernate.dialect.SQLServerDialect</property> <!-- Enable Hibernate's automatic session context management --> <property name="current_session_context_class">thread</property> <!-- Disable the second-level cache --> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <!-- Drop and re-create the database schema on startup --> <property name="hbm2ddl.auto">create</property> <mapping resource="com/model/StudentInfo.hbm.xml"/> </session-factory> </hibernate-configuration>
注意这个XML配置使用了一个不同的DTD。在这里。我们配置了Hibernate的SessionFactory-一个关联于特定数据库全局的工厂(factory)。
假设你要使用多个数据库,就要用多个的<session-factory>,通常把它们放在多个配置文件里(为了更easy启动)。
最開始的4个property元素包括必要的JDBC连接信息。方言(dialect)的property元素指明Hibernate 生成的特定SQL变量。你非常快会看到,Hibernate对持久化上下文的自己主动session管理就会派上用场。
打开hbm2ddl.auto选项将自己主动生成数据库模式(schema)- 直接增加数据库中。当然这个选项也能够被关闭(通过去除这个配置选项)或者通过Ant任务SchemaExport的帮助来把数据库schema重定向到文件里。
最后,在配置中为持久化类增加映射文件。
Demo下载地址:点击打开链接
五启动和辅助类
是时候来载入和储存一些StudentInfo对象了,但首先我们得编写一些基础的代码以完毕设置。我们必须启动Hibernate,此过程包含创建一个全局的SessoinFactory,并把它储存在应用程序代码easy訪问的地方。SessionFactory能够创建并打开新的Session。一个Session代表一个单线程的单元操作。SessionFactory则是个线程安全的全局对象,仅仅须要被实例化一次。
我们将创建一个HibernateUtil辅助类(helper class)来负责启动Hibernate和更方便地操作SessionFactory。让我们来看一下它的实现:
package com.util; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { // Create the SessionFactory from hibernate.cfg.xml sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) { // Make sure you log the exception, as it might be swallowed System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
这个类不但在它的静态初始化过程(仅当载入这个类的时候被JVM运行一次)中产生全局的SessionFactory,并且隐藏了它使用了静态singleton的事实。
它也可能在应用程序server中的JNDI查找SessionFactory。
演示样例的基本框架完毕了 - 如今我们能够用Hibernate来做些真正的工作。
六 载入并存储对象
我们最终能够使用Hibernate来载入和存储对象了。编写一个带有main()方法的StudentManager类:
package com.test; import org.hibernate.Session; import com.model.StudentInfo; import com.util.HibernateUtil; public class StudentManager { public static void main(String[] args) { StudentManager mgr = new StudentManager(); mgr.createAndStoreEvent(); HibernateUtil.getSessionFactory().close(); } private void createAndStoreEvent() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); StudentInfo student = new StudentInfo(); student.setName("无情"); student.setAge(24); student.setSex("女"); session.save(student); session.getTransaction().commit(); } }
我们创建了个新的StudentInfo对象并把它传递给Hibernate。
如今Hibernate负责与SQL打交道,并把INSERT命令传给数据库。在执行之前。让我们看一下处理Session和Transaction的代码。
一个Session就是个单一的工作单元。我们临时让事情简单一些,并如果HibernateSession和数据库事务是一一相应的。为了让我们的代码从底层的事务系统中脱离出来(此例中是JDBC,但也可能是JTA),我们使用Hibernate Session中的Transaction API。
sessionFactory.getCurrentSession()是干什么的呢?首先,仅仅要你持有SessionFactory(幸亏我们有HibernateUtil,能够随时获得),大可在不论什么时候、不论什么地点调用这种方法。
getCurrentSession()方法总会返回“当前的”工作单元。
记得我们在hibernate.cfg.xml中把这一配置选项调整为"thread"了吗?因此,因此,当前工作单元被绑定到当前运行我们应用程序的Java线程。可是。这并不是是全然准确的,你还得考虑工作单元的生命周期范围 (scope),它何时開始,又何时结束.
Session在第一次被使用的时候,即第一次调用getCurrentSession()的时候,其生命周期就開始。
然后它被Hibernate绑定到当前线程。当事务结束的时候,无论是提交还是回滚,Hibernate会自己主动把Session从当前线程剥离。而且关闭它。假若你再次调用getCurrentSession()。你会得到一个新的Session,而且開始一个新的工作单元。这样的线程绑定(thread-bound)的编程模型(model)是使用Hibernate的最广泛的方式,由于它支持对你的代码灵活分层(事务划分能够和你的数据訪问代码分离开来,在本教程的后面部分就会这么做)。 和工作单元的生命周期这个话题相关,Hibernate
Session是否被应该用来运行多次数据库操作?上面的样例对每一次操作使用了一个Session。这全然是巧合,这个样例不是非常复杂。无法展示其它方式。Hibernate Session的生命周期能够非常灵活。可是你绝不要把你的应用程序设计成为每一次数据库操作都用一个新的Hibernate Session。
七 Annotation注解方式
我们用Annotation注解方式来取代XML配置方式。
在持久化对象时进行注解:
package com.model; import javax.persistence.Entity; import javax.persistence.Id; /** * Hibernate Annotation注解方式 * @author xiaosi * */ //注解为一个实体 @ Entity public class TeacherInfo { private int id; private String name; private String sex; private int age; public TeacherInfo(){ } //注解主键 @ Id 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 String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
在Hibernate.cfg.xml配置中:
启动辅助类中有一个不同点:Configuration替换为AnnotationConfiguration
package com.util; import org.hibernate.SessionFactory; import org.hibernate.cfg.AnnotationConfiguration; public class HibernateAnoUtil { private static final SessionFactory sessionFactory; static { try { // Create the SessionFactory from hibernate.cfg.xml sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); } catch (Throwable ex) { // Make sure you log the exception, as it might be swallowed System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
载入存储对象:
package com.test; import org.hibernate.Session; import com.model.TeacherInfo; import com.util.HibernateAnoUtil; public class TeacherManager { public static void main(String[] args) { TeacherManager mgr = new TeacherManager(); mgr.createAndStoreEvent(); HibernateAnoUtil.getSessionFactory().close(); } private void createAndStoreEvent() { Session session = HibernateAnoUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); TeacherInfo teacher = new TeacherInfo(); teacher.setId(1); teacher.setName("王倩"); teacher.setAge(25); teacher.setSex("女"); session.save(teacher); session.getTransaction().commit(); } }
数据库插入的数据:
版权声明:本文博主原创文章,博客,未经同意不得转载。