好久没来了,我已经在达内学习两个多月了,今天李翊老师让我们写博,以后也会常来的。
我们的课程讲到web部分了,由于前面王海格老师讲解的JDBC、Hibernate的知识很深入,没能好好的消化,所以李翊老师帮我们串串线,带我们复习了两天的Hibernate。说实话,我的技术一直都不太好。以为原来看过点视频就觉得自己知识掌握的很多了,其实真的不是这样的。培训对于我这样的人来说真的很必要,因为我自己确实缺少程序员的某些正确的引导。
扯远了,言归正传,总结一下我所学到的Hibernate知识,希望对自己或者看到本篇BLOG的人有用处吧,本人技术一般,还请多提意见。
-----------------以下内容我会尽量的少说废话,不过我相信,我写的东西,行里人都能看的懂---------------------------
有很多人都说Hibernate不好用,慢。像李翊老师所说,其实你们还不会“玩”它。至于怎么个玩法,现在就逐一介绍。
强烈声明,本文章不是初级教程,是对经验的总结。完全没有接触过Hibernate的朋友请绕道。
Hibernate知识体系:1、五大接口(4接口、1类)
2、POJO
3、配置文件
4、映射文件
5、复杂映射
6、优化(*)
1、五大接口
·Session接口:Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句。)。但需要注意的是Session对象是非线程安全的。同时,Hibernate的session不同于JSP应用中的HttpSession。这里当使用session这个术语时,其实指的是Hibernate中的session,而以后会将HttpSesion对象称为用户session。
·SessionFactory接口:SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。
·Configuration接口:Configuration接口负责配置并启动Hibernate,创建SessionFactory对象。在Hibernate的启动的过程中,Configuration类的实例首先定位映射文档位置、读取配置,然后创建SessionFactory对象。
·Transaction接口:Transaction接口负责事务相关的操作。它是可选的,开发人员也可以设计编写自己的底层事务处理代码。
·Query和Criteria接口:Query和Criteria接口负责执行各种数据库查询。它可以使用HQL语言或SQL语句两种表达方式。
2、POJO
简单Java对象。只有私有属性,getter、setter方法,有参构造方法和无参构造方法。有的还需要重写hashcode和equals方法。
3、配置文件
是与数据库连接的相关参数。大概格式如下:
<?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>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.show_sql">true</property>
<property name="connection.url">
jdbc:mysql://localhost:3306/test
</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<mapping resource="org/whatisjava/domain/many2one.hbm.xml" />
<mapping resource="org/whatisjava/domain/one2many.hbm.xml" />
<mapping resource="org/whatisjava/domain/core.hbm.xml" />
</session-factory>
</hibernate-configuration>
其中,比较重要的属性是Hibernate.format_sql还有Hibernate.show_sql。前者是在执行测试类之后由IDE(我们用的MyEclipse)的控制台产生格式化好的SQL语句,后者是前者的保证,是在控制台显示SQL语句。我们知道Hibernate是对JDBC的封装,归根结底就是对数据库一系列操作,而操作数据库就要用到SQL语句,而Hibernate在后台做了什么,我们程序员当然需要去了解。因此我们才会关心这两个属性。其他属性,套用就可以了。
4、映射文件
每个POJO都需要有一个映射文件。其实我们大多数时候就是操作映射文件,而感觉POJO只是个凑数的角色,没它不行,有它也很少去修改它,就这么个东西。而映射文件是格式化好的文本文件,我们用工具去生成一些诸如建表、查询的时候,一般都是Hibernate对映射文件的操作。映射文件的内容如下:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.whatisjava.domain">
<class name="Order" table="t_order">
<id name="id" type="integer">
<generator class="native"></generator>
</id>
<set name="items" cascade="all" inverse="true">
<key column="order_id" foreign-key="fk_item_order" />
<one-to-many class="Item" />
</set>
<property name="createDate" type="timestamp">
<column name="create_date" />
</property>
</class>
<class name="Item" table="t_item">
<id name="id" type="integer">
<generator class="native"></generator>
</id>
<many-to-one name="order" column="order_id" class="Order" />
<property name="productName" type="string">
<column name="product_name" not-null="true" length="50" />
</property>
<property name="price" type="big_decimal">
<column name="unit_price" not-null="true" precision="7"
scale="2" />
</property>
<property name="amount" type="integer">
<column name="amount" />
</property>
</class>
<query name="findAllOrder">
<![CDATA[
select distinct o from Order o left outer join fetch o.items
]]>
</query>
</hibernate-mapping>
很抱歉,传了个这么复杂的映射文件。用我们老师的话讲,是这东西,看懂你能看懂的就行,不用考虑它是干什么的。我也是一样的目的,就是介绍映射文件的作用而已。其中,除了一些常规的与POJO对应的属性外。我想介绍一些老师今天给我们讲的一些鲜为人知的使用经验。大家可以借鉴一下。
在本映射文件中,大家有没有发现有些地方跟我们平常写的配置文件有些区别,好像是多了些什么参数,或者是格式有点不太一样是吧。没错,这些参数、格式,有的顾名思义,但是都是缺少官方或者详细的总结性的说法。因此我撰写本篇文章的目的就不言而喻了。(李老师说的好啊,Java这东西,照微软就是差在文档上,没一个像样的官方文档,愁~~~)
首先,大家应该发现了,在这一个映射文件中好像是对应了两个class文件。没错,在我们实际的开发之中,自己写的映射文件实在没必要写成两个。因为别人也会写映射文件,因此,是自己写的映射文件,就大胆的放在一个映射文件之中吧。
然后,看这里。
<property name="price" type="big_decimal">
<column name="unit_price" not-null="true" precision="7" scale="2" />
</property>
其实这里有两个小技巧。第一,我们平常好像把column是放在property中的吧,怎么拿出来单定义了呢?其实这里这样写是为了方便我们用一种叫Ant的工具。Ant老师也没给我们过多的介绍,只是知道它能够处理一些琐碎的事情。犹如一个秘书一样。我们用Ant只用到了,建立目录,通过映射文件在数据库中建立表,定义路径,通过映射文件建立POJO这些功能。我先列出代码吧。它就是一个名字为build.xml的文件。奇怪的是MyEclipse只要看到命名为build的xml文件就会在文件图标上有一只蚂蚁。
<?xml version="1.0" encoding="utf-8"?>
<project name="whatisjava.hibernate" default="clear" basedir=".">
<path id="cp">
<!--定义一组文件-->
<fileset dir="lib">
<include name="**/*.jar" />
</fileset>
<!--定义一个路径-->
<pathelement path="bin" />
</path>
<!--定义一个扩展的Ant任务(hibernate)-->
<taskdef name="hibernate" classname="org.hibernate.tool.ant.HibernateToolTask" classpathref="cp" />
<target name="clear">
<delete dir="bin" />
<mkdir dir="bin" />
</target>
<target name="copy">
<copy todir="bin">
<fileset dir="src">
<include name="**/*.xml" />
<include name="**/*.properties" />
</fileset>
</copy>
</target>
<target name="schema" depends="clear,copy">
<hibernate destdir=".">
<classpath refid="cp" />
<configuration configurationfile="bin/hibernate.cfg.xml" />
<hbm2ddl drop="true" create="true" console="true" export="true" outputfilename="db.sql" delimiter=";" format="true" />
</hibernate>
</target>
<target name="pojo" depends="clear,copy">
<hibernate destdir="src">
<classpath refid="cp" />
<configuration configurationfile="bin/hibernate.cfg.xml" />
<hbm2java jdk5="true" />
</hibernate>
</target>
</project>
大家能看懂吗?其实具体我也不知道为什么这样写,但是老师说了,只要你们会用就行了。所以,我也只是会用而已,并不了解它是为什么这样写的。我帮大家找了一些资料,大家看看比较官方的解释方法吧。
1.<project>标签
每个构建文件对应一个项目。<project>标签时构建文件的根标签。它可以有多个内在属性,就如代码中所示,其各个属性的含义分别如下。
(1) default表示默认的运行目标,这个属性是必须的。
(2) basedir表示项目的基准目录。
(3) name表示项目名。
(4) description表示项目的描述。
每个构建文件都对应于一个项目,但是大型项目经常包含大量的子项目,每一个子项目都可以有自己的构建文件。
2.<target>标签
一个项目标签下可以有一个或多个target标签。一个target标签可以依赖其他的target标签。
例如,有一个target用于编译程序,另一个target用于声称可执行文件。在生成可执行文件之前必须先编译该文件,因策可执行文件的target依赖于编译程序的target。Target的所有属性如下。
(1).name表示标明,这个属性是必须的。
(2).depends表示依赖的目标。
(3)if表示仅当属性设置时才执行。
(4)unless表示当属性没有设置时才执行。
(5)description表示项目的描述。
Ant的depends属性指定了target的执行顺序。Ant会依照depends属性中target出现顺序依次执行每个target。在执行之前,首先需要执行它所依赖的target。程序中的名为run的target的depends属性compile,而名为compile的target的depends属性是prepare,所以这几个target执行的顺序是prepare->compile->run。
一个target只能被执行一次,即使有多个target依赖于它。如果没有if或unless属性,target总会被执行。
先列出这两个吧,列多了大家迷糊。project标签姑且不说,他是Ant的标志。最重要的是target标签。其实一个Ant文件就是一大堆target的集合。一个target就是一个操作,比如编译文件,通过映射文件生成POJO或者通过映射文件生成数据库中表。大家还是要了target比较重要。
先不说Ant了,大家有兴趣可以google。
再说说我们的映射文件。
<property name="price" type="big_decimal">
<column name="unit_price" not-null="true" precision="7" scale="2" />
</property>
还是上面的那句。我们把column放中间写就是为了方便Ant自动在数据库中产生表。unit_price字段相应的设置,非空,percision是标度,scale是小数点后面几位。格式就是XXXXX.XX这样。大概可以这样解释。
在看这句
<query name="findAllOrder">
<![CDATA[
select distinct o from Order o left outer join fetch o.items
]]>
</query>
我们知道正常文本很少能出现<![CDATA[xxxx]]>这样奇怪的字符序列,所以设计者们用这个防止一些特殊符号如<,>,&等在XML文件中显示的问题。
关键不是它,是query这个标签的作用。这个标签可以让我们不用在测试类中生成hql语句,而由映射文件来生成。这里还要注意hlq的select后面可以跟new出来的东西,只要是new的那个对象对应的类有相应的构造方法就可以。所及,请大家记住这个标签的作用。
我拿出来的例子是many2one,one2many的例子。还有一个映射文件是十分重要的,具有很多有用的小功能,大家需要去掌握。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.whatisjava.domain">
<class name="Message" table="t_message" dynamic-insert="true" dynamic-update="true">
<id name="id" type="integer" column="id">
<generator class="native" />
</id>
<property name="title" type="string">
<column name="msg_title" length="50" />
</property>
<property name="content" type="string">
<column name="msg_content" length="200" />
</property>
<property name="createDate" type="timestamp">
<column name="create_date" />
</property>
</class>
</hibernate-mapping>
注意到没有,在class标签元素中,有这么两个属性, dynamic-insert="true" dynamic-update="true"。他们是干什么的呢。我们知道,Hibernate在MyEclipse控制台中生成SQL语句的时候总是那么的糟糕。我们经常使用JDBC的时候用preparedStatement来处理SQL,用了它之后我们可以使用预编译的SQL语句从而减少对数据库的操作。而这样有个弊端就是,如果我们需要插入或者查询10个字段中的两个字段的时候,总是显得力不从心,始终会出现10个问号(对应10个字段),如果字段多了,查询起来会大幅降低查询效率。因此我们用到这两个class标签属性来解决这个问题。其实这个属性对blob和clob类型数据更加的有效。
映射文件先告一段落吧。我们最关键的是优化,其实上面已经涉及很多优化的解决方案了。后面的只是补充而已。
5、复杂映射(穿插讲解吧) 6、优化
关于sessionFactory.大家知道为什么它只定义一个吗?原因有三。一、它是个大对象。二、它要保证线程安全。三、要让它包装的对象是唯一个。而我们通常开发的时候,静态的变量由于只加载一次因此它是唯一的,就是这个道理。
Hibernate中复杂映射使用频率排序(方便大家有针对性的学习Hibernate):
many-to-one > one-to-many > many-to-many > one-to-one
(累了 ,明天过来继续更新)