为什么引入hibernate?
模型不匹配:Java是面向对象的语言,对象模型,其主要概念有:继承/关联/多态等;数据库是关系模型,其主要概念有:表/主键/外键等。
解决办法?
• 使用JDBC手工转换。
• 使用ORM(Object Relation Mapping对象关系映射)框架来解决,主流的ORM框架有Hibernate/TopLink/OJB。
使用Hibernate需要具备的包:
hibernate_home\hibernate3.jar和hibernate_home\lib\下的如下包:
antlr.jar, cglib.jar, asm.jar, commons-collections.jar, commons-logging.jar, jta.jar, dom4j.jar
同时导入dom4j.jar和log4j.jar会导致如下错误:
1 log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
2 log4j:WARN Please initialize the log4j system properly.
解决办法:把log4j.properties放到类路径下
log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
log4j.logger.org.hibernate=info
#log4j.logger.org.hibernate=debug
### log HQL query parser activity
#log4j.logger.org.hibernate.hql.ast.AST=debug
### log just the SQL
log4j.logger.org.hibernate.SQL=debug
### log JDBC bind parameters ###
log4j.logger.org.hibernate.type=info
### log schema export/update ###
log4j.logger.org.hibernate.tool.hbm2ddl=info
### log HQL parse trees
#log4j.logger.org.hibernate.hql=debug
### log cache activity ###
log4j.logger.org.hibernate.cache=info
### log transaction activity
#log4j.logger.org.hibernate.transaction=debug
### log JDBC resource acquisition
#log4j.logger.org.hibernate.jdbc=debug
### enable the following line if you want to track down connection ###
### leakages when using DriverManagerConnectionProvider ###
#log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=
开发配置:
配置文件hibernate.cfg.xml和hibernate.properties,两个文件的作用一样,提供一个即可,推荐xml格式的。
hibernate_home\etc下是示例配置文件。
可以在配置文件中指定:
数据库的URL/用户名/密码/JDBC驱动类/方言(dialect)等。
启动时hibernate会在classpath中寻找这个配置文件。
映射文件(*.hbm.xml,对象模型和关系模型的映射)。在hibernate_home\eg下有完整的hibernate示例。
实例:
工程结构
将hibernate_home\hibernate3.jar和hibernate_home\lib下的jar文件导入到工程中。
Domain Object User.java为实体对象,一个普通的Java bean
User.java
1 package cn.jvpy.hibernate.domain;
2
3 import java.util.Date;
4
5 public class User {
6 private int id;
7 private String name;
8 private Date birthday;
9
10 public int getId() {
11 return id;
12 }
13
14 public void setId(int id) {
15 this.id = id;
16 }
17
18 public String getName() {
19 return name;
20 }
21
22 public void setName(String name) {
23 this.name = name;
24 }
25
26 public Date getBirthday() {
27 return birthday;
28 }
29
30 public void setBirthday(Date birthday) {
31 this.birthday = birthday;
32 }
33 }
34
Domain Object的限制:
• 该类中必须得有一个不带参数的ctor。
• 有标志符id与数据库的主键进行映射(可选)。
• 该类不要设置成final类型,对懒加载有影响(可选)。
映射文件的作用是将User对象与数据库表建立映射关系,一般命名格式为*.hbm.xml
User.hbm.xml
1 <?xml version="1.0"?>
2 <!DOCTYPE hibernate-mapping PUBLIC
3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
4 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
5 <hibernate-mapping
6 package="cn.jvpy.hibernate.domain">
7
8 <class name="User">
9
10 <id name="id">
11 <generator class="native"/>
12 </id>
13
14 <property name="name" />
15 <property name="birthday" />
16
17 </class>
18
19 </hibernate-mapping>
该配置文件在domain object的角度看待问题,缺省的类名和表名是一样的,可以通过table属性指定。
配置文件
hibernate.cfg.xml
1 <!DOCTYPE hibernate-configuration PUBLIC
2 "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
3 "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
4
5 <hibernate-configuration>
6 <session-factory>
7 <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
8 <!-- 三个"/"表示本机地址默认的3306端口 -->
9 <property name="connection.url">jdbc:mysql:///test</property>
10 <property name="connection.username">root</property>
11 <property name="connection.password">root</property>
12
13 <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
14
15 <!-- 如果没有下面这行,并且test表没有事先创建,则抛出异常:
16 com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: Table 'test.user' doesn't exist -->
17 <property name="hibernate.hbm2ddl.auto">create</property>
18
19 <!-- 漏掉下面的mapping会抛出异常:Unknown entity: cn.jvpy.hibernate.domain.User -->
20 <mapping resource="cn/jvpy/hibernate/domain/User.hbm.xml" />
21 </session-factory>
22 </hibernate-configuration>
测试代码
Base.java
1 package cn.jvpy.hibernate;
2
3 import java.util.Date;
4
5 import org.hibernate.Session;
6 import org.hibernate.SessionFactory;
7 import org.hibernate.Transaction;
8 import org.hibernate.cfg.Configuration;
9
10 import cn.jvpy.hibernate.domain.User;
11
12 public class Base {
13 public static void main(String[] args) {
14 Configuration cfg = new Configuration();
15 // 漏掉这句会抛出异常:
16 // org.hibernate.HibernateException: Hibernate Dialect must be explicitly set
17 cfg.configure();
18 SessionFactory sf = cfg.buildSessionFactory();
19 Session s = sf.openSession();
20
21 // 如果不开启事务,则数据库表中的内容不会被更新
22 Transaction tx = s.beginTransaction();
23 User user = new User();
24 user.setBirthday(new Date());
25 user.setName("name");
26
27 s.save(user);
28 tx.commit();
29
30 s.close();
31
32 System.out.println("END");
33 }
34 }
问题及原因?
•
抛出异常:org.hibernate.HibernateException: Hibernate Dialect must be explicitly set
错误原因:其实并不是没有对dialect进行设置,而是在使用hibernate的时候,没有调用配置文件,导致系统不能读出dialect信息。
解决办法:
Code
1 Configuration cfg = new Configuration();
2 // 漏掉这句会抛出异常:
3 // org.hibernate.HibernateException: Hibernate Dialect must be explicitly set
4 cfg.configure();
5 SessionFactory sf = cfg.buildSessionFactory();
6 Session s = sf.openSession();
• 如果没有开启事务,并且hibernate能成功更新了数据库,则应该在mysql中查看数据库表的创建方式是否有问题:
1 show create table user;
正常的显示如下:
Code
1 CREATE TABLE `user` (
2 `id` int(11) NOT NULL AUTO_INCREMENT,
3 `name` varchar(255) DEFAULT NULL,
4 `birthday` datetime DEFAULT NULL,
5 PRIMARY KEY (`id`)
6 ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=gbk |
之所以错误是因为engine不是InnoDB,这个引擎具有回滚功能。用
show engines命令可以查看mysql支持的engine.
上面的测试代码不太规范,优化的写法如下:
添加一个工具类用于进行hibernate初始化(耗时的过程,只需要做一次)
HibernateUtil.java
1 package cn.jvpy.hibernate;
2
3 import org.hibernate.Session;
4 import org.hibernate.SessionFactory;
5 import org.hibernate.cfg.Configuration;
6
7 public final class HibernateUtil {
8 private static SessionFactory sessionFactory;
9 private static Session session;
10
11 // 没必要创建该类的实例
12 private HibernateUtil() {
13 }
14
15 // 只会在该类被加载的时候执行
16 static {
17 Configuration cfg = new Configuration();
18 // 漏掉这句会抛出异常:
19 // org.hibernate.HibernateException: Hibernate Dialect must be explicitly set
20 cfg.configure();
21 sessionFactory = cfg.buildSessionFactory();
22 session = sessionFactory.openSession();
23 }
24
25 public SessionFactory getSessionFactory() {
26 return sessionFactory;
27 }
28
29 public static Session getSession() {
30 return session;
31 }
32 }
这时添加用户的操作可以这么写:
Base.java
1 package cn.jvpy.hibernate;
2
3 import java.util.Date;
4
5 import org.hibernate.HibernateException;
6 import org.hibernate.Session;
7 import org.hibernate.Transaction;
8
9 import cn.jvpy.hibernate.domain.User;
10
11 public class Base {
12 public static void main(String[] args) {
13 User user = new User();
14 user.setBirthday(new Date());
15 user.setName("name");
16
17 addUser(user);
18 }
19
20 static void addUser(User user) {
21 Session session = null;
22 Transaction tx = null;
23
24 try {
25 session = HibernateUtil.getSession();
26 tx = session.beginTransaction();
27 session.save(user);
28 tx.commit();
29 } catch (HibernateException e) {
30 if (tx != null) {
31 tx.rollback();// 回滚
32 }
33 throw e;// 抛出异常
34 } finally {
35 if (session != null) {
36 session.close();
37 }
38 }
39 }
40
41 static void addUser1(User user) {
42 Session session = null;
43 Transaction tx = null;
44
45 try {
46 session = HibernateUtil.getSession();
47 tx = session.beginTransaction();
48 session.save(user);
49 // 遇到异常,说明这句还没有执行,数据库没有收到提交请求时会默认回滚
50 tx.commit();
51 } finally {
52 if (session != null) {
53 session.close();
54 }
55 }
56 }
57
58 }
59