最近开始学习hibernate,然后就想把这个学习历程总结下来。还是打算主要分两个过程,第一个过程:学会使用hibernate,第二个过程:做一些源码分析,更加深入的理解。
言归正传,开始入门案例,这里的入门案例不牵扯其他框架,所以一开始学习的时候尽量不要牵扯其他框架,如SpringMVC。如果入门时牵涉其他框架,有时候会搞不清楚hibernate和其他框架的责任机制,本工程虽然搭建的是SpringMVC项目,主要是为后来服务,但是目前几乎用不到:
hibernate有两种配置文件,一种就是像jdbc那样连接数据库的配置文件,另一种就是domain类和数据库表之间的映射文件。
第一种配置文件,又有两种形式,可以是properties文件,也可以是xml文件,先来说下xml形式的文件,文件名为hibernate.cfg.xml,可以是其他文件名,如下:
前面四个属性和jdbc配置是一样的,后面的hibernate.dialect即选择使用哪种方言,hibernate中有些特性是需要特定的数据库支持的,因此需要明确指定使用哪种方言,不同的数据库甚至相同的数据库不同的版本、不同的引擎,他们都有各自的方言实现,如mysql就有5种形式的方言。目前我还不是特别清楚,所以就暂时使用org.hibernate.dialect.MySQLDialect类,所有的方言都继承了Dialect类,如下所示:
hibernate.show_sql属性为true则是将产生的sql语句显示出来。
properties形式的,文件名是hibernate.properties文件,目前我还没找到是否允许是其他文件名。如下:
和上面的xml文件没有什么区别。
第二种配置文件,即domain类和数据库表的映射文件,先说domain类,如Customer类,如下:
对应的映射文件是:Customer.hbm.xml,可以是其他文件名,
其中class标签的name指明了哪个domain类,table指明该domain类所对应的数据库的表。
id标签中的generator 来专门说明主键的生成策略,如可以是increment、identity、sequence、native等。
increment:则是由hibernate自增来生成主键。
identity:则是由底层数据库自增来生成主键,支持的数据库有:DB2、mysql、SqlServer等。
sequence:也是有底层数据库来生成主键,支持的数据库有:DB2、oracle等。
native:根据底层数据库所支持的能力,自动从identity、sequence中挑选一个作为主键的生成策略。
然后就是property标签:name指明Customer的属性,column指明表的列名,type则是java类型和表中字段类型的桥梁。如java中的String类型,如果要映射到数据库中的varchar类型,则需要type为string,如果要映射到数据库中的text类型,则type为text。具体的类型间的映射关系可以到网上搜一搜。
准备工作做完了,然后就要开始使用hibernate了,我的maven工程使用的是4.3.6.Final版本的hibernate,后面提供下载。
hibernate的几个重要概念Configuration、SessionFactory、Session、Transaction、Query和Criteria:
Configuration:用于配置和启动hibernate,然后可以创建出SessionFactory。
SessionFactory:作为产生session的工厂,一个SessionFactory实例代表一个数据库存储源,如果要访问多个数据库,则需要为每个数据库创建一个单独的SessionFactory实例。它是重量级的,内部有很多缓存,不可随意创建和销毁。
Session:用于增删改对象。
Transaction:用于事务的功能。
Query和Criteria:用于查询数据。
首先,我声明了这样的一个接口HibernateDao,用于获取Session,如下:
它的实现类HibernateDaoImpl如下:
这里主要是对hibernate指明之前已说明的两种配置文件,一种连接数据库的配置文件,一种domain类对应数据库中表的配置文件。
对于连接数据库的配置文件:如何接收properties形式的配置文件,又如何接收xml形式的配置文件。
(1)接收properties形式的配置文件:对于上述的代码,config.buildSessionFactory()(这是一个过时的方法)会去尝试在类路径的根目录下加载hibernate.properties配置文件。我目前只知道这种形式来接收properties形式的配置文件,路径和文件名都是限制死的,证据如下:
这里用到了Environment类,所以类加载器要去加载Environment类,并会执行它的静态代码块,如下:
(2)接收xml形式的配置文件:需要使用config.configure(),如下所示:
config.configure()会默认从类路径的根目录下去加载hibernate.cfg.xml配置文件,源代码如下:
config.configure()会调用含有参数的configure()方法,所以如果我们想在类路径下的子目录中去放置我们的配置文件,则需要使用含参数的configure(相对类路径的路径)。并且文件名可以随意指定,如我们的配置文件可以放到类路径下/hibernate/config/文件夹中,我可以这样配置config.configure("/hibernate/config/hibernate.cfg.xml")。
对于domain类和数据库表之间的映射文件的加载,可以使用config.addClass(Customer.class),该方法会去domain所在路径下寻找Customer.hbm.xml映射文件,源代码如下:
persistentClass.getName()得到类的完整名称,对于本工程来说是com.ligang.domain.Customer,最终得到的mappingResourceName 为com/ligang/domain/Customer.hbm.xml。
如果该映射文件不像放到domain类的同一个目录下,想放到类路径下的hibernate/mapping/文件夹下则可以使用config.addResource("hibernate/mapping/Customer.hbm.xml"),这里路径的前面不能含有/,文件名可以随意取,没有要求。
加载配置文件说完了,然后继续下面的步骤,开始创建SessionFactory了。
对于上述方法,已是过时的方法,推荐的方法是buildSessionFactory(serviceRegistry),网上有很多的说法是,使用如下代码替代:
但是目前对于4.3.6.Final的hibernate来说ServiceRegistryBuilder已经过时,替换类则是StandardServiceRegistryBuilder,可以使用如下方案:
我们再看下config.buildSessionFactory()的源码:
它内部也是使用了StandardServiceRegistryBuilder,所以说上述方案还没config.buildSessionFactory()这个过时的方案好。对于ServiceRegistry这一块,日后再详细研究,至少我感觉hibernate这一块设计的不是很好。我理解的好的设计,对于用户来说是简单易用的,尽量对外不要暴漏内部的一些机制,而这里ServiceRegistry则会让一个初学者一头雾水,需要花费更多的时间去理解内部的机制。
有了SessionFactory,我们便可以获取Session,然后进行增删改查了,建立了如下的单元测试类如下:
运行单元测试方法,便可以看到成功的进行了增添操作。
言归正传,开始入门案例,这里的入门案例不牵扯其他框架,所以一开始学习的时候尽量不要牵扯其他框架,如SpringMVC。如果入门时牵涉其他框架,有时候会搞不清楚hibernate和其他框架的责任机制,本工程虽然搭建的是SpringMVC项目,主要是为后来服务,但是目前几乎用不到:
hibernate有两种配置文件,一种就是像jdbc那样连接数据库的配置文件,另一种就是domain类和数据库表之间的映射文件。
第一种配置文件,又有两种形式,可以是properties文件,也可以是xml文件,先来说下xml形式的文件,文件名为hibernate.cfg.xml,可以是其他文件名,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
<hibernate-configuration>
<session-factory name=
"hibernateSessionFactory"
>
<property name=
"hibernate.connection.driver_class"
>com.mysql.jdbc.Driver</property>
<property name=
"hibernate.connection.password"
>ligang</property>
<property name=
"hibernate.connection.url"
>jdbc:mysql:
//localhost:3306</property>
<property name=
"hibernate.connection.username"
>root</property>
<property name=
"hibernate.dialect"
>org.hibernate.dialect.MySQLDialect</property>
<property name=
"hibernate.show_sql"
>
true
</property>
<property name=
"hibernate.default_schema"
>hibernate</property>
</session-factory>
</hibernate-configuration>
|
前面四个属性和jdbc配置是一样的,后面的hibernate.dialect即选择使用哪种方言,hibernate中有些特性是需要特定的数据库支持的,因此需要明确指定使用哪种方言,不同的数据库甚至相同的数据库不同的版本、不同的引擎,他们都有各自的方言实现,如mysql就有5种形式的方言。目前我还不是特别清楚,所以就暂时使用org.hibernate.dialect.MySQLDialect类,所有的方言都继承了Dialect类,如下所示:
hibernate.show_sql属性为true则是将产生的sql语句显示出来。
properties形式的,文件名是hibernate.properties文件,目前我还没找到是否允许是其他文件名。如下:
1
2
3
4
5
6
|
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql:
//localhost:3306/hibernate?useUnicode=true&characterEncoding=utf-8
hibernate.connection.username=root
hibernate.connection.password=ligang
hibernate.show_sql=
true
|
和上面的xml文件没有什么区别。
第二种配置文件,即domain类和数据库表的映射文件,先说domain类,如Customer类,如下:
1
2
3
4
5
6
7
8
|
public
class
Customer {
private
Long id;
private
String name;
private
String email;
private
Timestamp registeredTime;
//省略get、set方法
}
|
对应的映射文件是:Customer.hbm.xml,可以是其他文件名,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
<hibernate-mapping>
<
class
name=
"com.ligang.domain.Customer"
table=
"customer"
>
<id name=
"id"
column=
"id"
type=
"long"
>
<generator
class
=
"identity"
/>
</id>
<property name=
"name"
column=
"name"
type=
"string"
/>
<property name=
"email"
column=
"email"
type=
"string"
/>
<property name=
"registeredTime"
column=
"registeredTime"
type=
"timestamp"
/>
</
class
>
</hibernate-mapping>
|
其中class标签的name指明了哪个domain类,table指明该domain类所对应的数据库的表。
id标签中的generator 来专门说明主键的生成策略,如可以是increment、identity、sequence、native等。
increment:则是由hibernate自增来生成主键。
identity:则是由底层数据库自增来生成主键,支持的数据库有:DB2、mysql、SqlServer等。
sequence:也是有底层数据库来生成主键,支持的数据库有:DB2、oracle等。
native:根据底层数据库所支持的能力,自动从identity、sequence中挑选一个作为主键的生成策略。
然后就是property标签:name指明Customer的属性,column指明表的列名,type则是java类型和表中字段类型的桥梁。如java中的String类型,如果要映射到数据库中的varchar类型,则需要type为string,如果要映射到数据库中的text类型,则type为text。具体的类型间的映射关系可以到网上搜一搜。
准备工作做完了,然后就要开始使用hibernate了,我的maven工程使用的是4.3.6.Final版本的hibernate,后面提供下载。
hibernate的几个重要概念Configuration、SessionFactory、Session、Transaction、Query和Criteria:
Configuration:用于配置和启动hibernate,然后可以创建出SessionFactory。
SessionFactory:作为产生session的工厂,一个SessionFactory实例代表一个数据库存储源,如果要访问多个数据库,则需要为每个数据库创建一个单独的SessionFactory实例。它是重量级的,内部有很多缓存,不可随意创建和销毁。
Session:用于增删改对象。
Transaction:用于事务的功能。
Query和Criteria:用于查询数据。
首先,我声明了这样的一个接口HibernateDao,用于获取Session,如下:
1
2
3
4
|
public
interface
HibernateDao {
public
Session getSession();
}
|
它的实现类HibernateDaoImpl如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Repository
public
class
HibernateDaoImpl
implements
HibernateDao{
private
SessionFactory sessionFactory;
public
HibernateDaoImpl(){
Configuration config=
new
Configuration();
config.addClass(Customer.
class
);
sessionFactory=config.buildSessionFactory();
}
@Override
public
Session getSession() {
return
sessionFactory.openSession();
}
}
|
这里主要是对hibernate指明之前已说明的两种配置文件,一种连接数据库的配置文件,一种domain类对应数据库中表的配置文件。
对于连接数据库的配置文件:如何接收properties形式的配置文件,又如何接收xml形式的配置文件。
(1)接收properties形式的配置文件:对于上述的代码,config.buildSessionFactory()(这是一个过时的方法)会去尝试在类路径的根目录下加载hibernate.properties配置文件。我目前只知道这种形式来接收properties形式的配置文件,路径和文件名都是限制死的,证据如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public
SessionFactory buildSessionFactory()
throws
HibernateException {
Environment.verifyProperties( properties );
ConfigurationHelper.resolvePlaceHolders( properties );
final
ServiceRegistry serviceRegistry =
new
StandardServiceRegistryBuilder()
.applySettings( properties )
.build();
setSessionFactoryObserver(
new
SessionFactoryObserver() {
@Override
public
void
sessionFactoryCreated(SessionFactory factory) {
}
@Override
public
void
sessionFactoryClosed(SessionFactory factory) {
( (StandardServiceRegistryImpl) serviceRegistry ).destroy();
}
}
);
return
buildSessionFactory( serviceRegistry );
}
|
这里用到了Environment类,所以类加载器要去加载Environment类,并会执行它的静态代码块,如下:
(2)接收xml形式的配置文件:需要使用config.configure(),如下所示:
1
2
3
4
5
6
|
public
HibernateDaoImpl(){
Configuration config=
new
Configuration();
config.configure();
config.addClass(Customer.
class
);
sessionFactory=config.buildSessionFactory();
}
|
config.configure()会默认从类路径的根目录下去加载hibernate.cfg.xml配置文件,源代码如下:
1
2
3
4
|
public
Configuration configure()
throws
HibernateException {
configure(
"/hibernate.cfg.xml"
);
return
this
;
}
|
config.configure()会调用含有参数的configure()方法,所以如果我们想在类路径下的子目录中去放置我们的配置文件,则需要使用含参数的configure(相对类路径的路径)。并且文件名可以随意指定,如我们的配置文件可以放到类路径下/hibernate/config/文件夹中,我可以这样配置config.configure("/hibernate/config/hibernate.cfg.xml")。
对于domain类和数据库表之间的映射文件的加载,可以使用config.addClass(Customer.class),该方法会去domain所在路径下寻找Customer.hbm.xml映射文件,源代码如下:
1
2
3
4
5
|
public
Configuration addClass(Class persistentClass)
throws
MappingException {
String mappingResourceName = persistentClass.getName().replace(
'.'
,
'/'
) +
".hbm.xml"
;
LOG.readingMappingsFromResource( mappingResourceName );
return
addResource( mappingResourceName, persistentClass.getClassLoader() );
}
|
persistentClass.getName()得到类的完整名称,对于本工程来说是com.ligang.domain.Customer,最终得到的mappingResourceName 为com/ligang/domain/Customer.hbm.xml。
如果该映射文件不像放到domain类的同一个目录下,想放到类路径下的hibernate/mapping/文件夹下则可以使用config.addResource("hibernate/mapping/Customer.hbm.xml"),这里路径的前面不能含有/,文件名可以随意取,没有要求。
加载配置文件说完了,然后继续下面的步骤,开始创建SessionFactory了。
1
|
sessionFactory=config.buildSessionFactory();
|
对于上述方法,已是过时的方法,推荐的方法是buildSessionFactory(serviceRegistry),网上有很多的说法是,使用如下代码替代:
1
|
ServiceRegistry serviceRegistry=
new
ServiceRegistryBuilder().applySettings(config.getProperties()).buildServiceRegistry();
|
但是目前对于4.3.6.Final的hibernate来说ServiceRegistryBuilder已经过时,替换类则是StandardServiceRegistryBuilder,可以使用如下方案:
1
2
|
ServiceRegistry serviceRegistry=
new
StandardServiceRegistryBuilder().applySettings(config.getProperties()).build();
sessionFactory=config.buildSessionFactory(serviceRegistry);
|
我们再看下config.buildSessionFactory()的源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public
SessionFactory buildSessionFactory()
throws
HibernateException {
Environment.verifyProperties( properties );
ConfigurationHelper.resolvePlaceHolders( properties );
final
ServiceRegistry serviceRegistry =
new
StandardServiceRegistryBuilder()
.applySettings( properties )
.build();
setSessionFactoryObserver(
new
SessionFactoryObserver() {
@Override
public
void
sessionFactoryCreated(SessionFactory factory) {
}
@Override
public
void
sessionFactoryClosed(SessionFactory factory) {
( (StandardServiceRegistryImpl) serviceRegistry ).destroy();
}
}
);
return
buildSessionFactory( serviceRegistry );
}
|
它内部也是使用了StandardServiceRegistryBuilder,所以说上述方案还没config.buildSessionFactory()这个过时的方案好。对于ServiceRegistry这一块,日后再详细研究,至少我感觉hibernate这一块设计的不是很好。我理解的好的设计,对于用户来说是简单易用的,尽量对外不要暴漏内部的一些机制,而这里ServiceRegistry则会让一个初学者一头雾水,需要花费更多的时间去理解内部的机制。
有了SessionFactory,我们便可以获取Session,然后进行增删改查了,建立了如下的单元测试类如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@RunWith
(JUnit4ClassRunner.
class
)
@ContextConfiguration
(locations =
"file:src/main/webapp/WEB-INF/mvc-servlet.xml"
)
public
class
CustomerDaoTest {
@Autowired
private
HibernateDao hibernateDao;
@Test
public
void
testAdd(){
Customer customer=
new
Customer();
customer.setName(
"小明"
);
customer.setEmail(
"123456789@qq.com"
);
Timestamp t=
new
Timestamp(System.currentTimeMillis());
customer.setRegisteredTime(t);
Session session=hibernateDao.getSession();
Transaction tx=session.beginTransaction();
session.save(customer);
tx.commit();
session.close();
}
}
|
运行单元测试方法,便可以看到成功的进行了增添操作。