首页 > 代码库 > Hibernate技术(一)--Hibernate初步
Hibernate技术(一)--Hibernate初步
Hibernate初步
ORM:在编写程序时,处理数据采用面向对象的方式,而保存数据却以关系型数据库的方式,因此需要一种能在两者之间进行转换的机制。这种机制称为ORM,ORM保存了对象和关系型数据库表的映射信息。
Hibernate框架搭建
Hibernate3JAR包引入:
antlr-2.7.6.jar | 语言转换工具,实现HQL到SQL的转换 |
commons-collections-3.1.jar |
|
dom4j-1.6.1.jar | 读写XML文件 |
hibernate3.jar | 核心类库 |
hibernate-jpa-2.0-api-1.0.0.Final.jar | 对JPA规范的支持,事物处理 |
javassist-3.12.0.GA.jar | 分析、编辑、创建java字节码的类库 |
jta-1.1.jar |
|
mysql-connector-java-5.1.12-bin.jar | 数据库驱动 |
slf4j-api-1.6.1.jar |
|
Hibernate4 jar包引入:
文件配置
Hibernate.cfg.xml
<!DOCTYPEhibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 数据库连接配置 --> <!—数据库类型 --> <propertyname="hibernate.connection.driver_class">com.mysql.jdbc.Driver </property> <!—数据库URL --> <propertyname="hibernate.connection.url">jdbc:mysql:///hib_demo </property> <!—用户名 --> <propertyname="hibernate.connection.username">root </property> <!—用户名密码 --> <propertyname="hibernate.connection.password">root </property> <!—配置Hibernate数据库类型 --> <propertyname="hibernate.dialect">org.hibernate.dialect.MySQLDialect </property> <!—显示SQL脚本语句 --> <propertyname="hibernate.show_sql">true </property>
<!-- 加载所有映射 --> <mappingresource="映射文件目录"/>
</session-factory> </hibernate-configuration> |
sf = new Configuration() .configure() .addClass(Employee.class) .buildSessionFactory()//(测试时候用)会自动加载映射文件:效果等同于 <mappingresource="映射文件目录"/> |
|
数据库连接参数配置
例如:
sql语言配置
#hibernate.dialectorg.hibernate.dialect.MySQLDialect
#hibernate.dialectorg.hibernate.dialect.MySQLInnoDBDialect
#hibernate.dialectorg.hibernate.dialect.MySQLMyISAMDialect
自动建表
Hibernate.properties
hibernate.hbm2ddl.auto create-drop 每次在创建sessionFactory时候执行创建表;
当调用sesisonFactory的close方法的时候,删除表!
hibernate.hbm2ddl.auto create 每次都重新建表;如果表已经存在就先删除再创建
hibernate.hbm2ddl.auto update 如果表不存在就创建;表存在就不创建;
hibernate.hbm2ddl.auto validate (生成环境时候) 执行验证: 当映射文件的内容与数据 库表结构不一样的时候就报错!
代码自动建表和文件配置自动建表区别
文件配置自动建表在加载配置文件时候才会建表,代码自动建表启动时候就会建表。
代码自动建表:
// 创建配置管理类对象
Configuration config = new Configuration();
// 加载主配置文件
config.configure();
// 创建工具类对象
SchemaExport export =new SchemaExport(config);
// 建表
// 第一个参数:是否在控制台打印建表语句
// 第二个参数:是否执行脚本
export.create(true,true);
对象的映射 (映射文件)
类名.hbm.xml
<?xmlversion="1.0"?> <!DOCTYPEhibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mappingpackage="cn.itcast.a_hello" auto-import="trueHQL查询时不用指定包名/false得指定"> <classname="实体类的名字"table="数据库表名"catalog="数据表的名字"> <!-- 主键 ,映射id属性必须有一个--> <idname="实体类属性"type="属性的java类型"column="指定对应数据库表的主键"type=“字段类型”> <generatorclass="主键生成器策略"/>
主键生成策略: increment 个是由Hibernate在内存中生成主键,每次增量为1,不依赖于底层的数据库,因此所有的数据库都可以使用 identity 采用数据库生成的主键,用于为long、short、int类型生成唯一标识, Oracle 不支持自增字段. sequence 对象标识符由底层的序列生成机制产生,要求底层数据库支持序列 native 根据底层数据库的能力,从identity、sequence、hilo中选择一个,灵活性更强。 assigned 指定主键生成策略为手动指定主键的值 uuid.hex 使用一个128-bit的UUID算法生成字符串类型的标识符 uuid.string hibernate会算出一个16位的值插入 foreign 即把别的表的主键作为当前表的主键; </id> <!-- 非主键,映射 --> <propertyname="实体类属性"column="指定对应数据库表的主键"></property> <set name="集合属性名"table="集合属性要映射到的表"> <key column="指定集合表的外键字段"></key> <element column="address"type="元素类型,一定要指定"></element> </set> <!-- list集合映射 list-index 指定的是排序列的名称 (因为要保证list集合的有序) --> <list name=".." table=".."> <key column=".."></key> <list-index column=".."></list-index> <element column=".." type="string"></element> </list> <!-- map集合的映射--> <map name=".." table=".."> <key column=".."></key> <map-key column=".." type=".." ></map-key> <element column=".." type=".." ></element> </map> </class>
|
type 指定映射表的字段的类型,如果不指定会匹配属性的类型 java类型: 必须写全名 hibernate类型: 直接写类型,都是小写 <!-- 如果列名称为数据库关键字,需要用反引号或改列名。 --> <propertyname="desc" column="`desc`"type="java.lang.String"></property> 联合/复合主键 如果找不到合适的列作为主键,除了用id列以外,我们一般用联合主键,即多列的值作为一个主键,从而确保记录的唯一性。 <!-- 复合主键映射 --> <composite-id name="keys"> <key-property name="userName" type="string"></key-property> <key-property name="address" type="string"></key-property> </composite-id> |
运行流程
执行流程:
会话工厂类SessionFactory创建
Hibernate4.0以下版本创建
public static void main(String[] args)throws Exception{ // 1. 加载默认的hibernate.cfg.xml的配置文件 Configuration config = new Configuration().configure(); // 2. 加载hbm文件 (Test.hbm.xml) config.addClass(cn.itcast.hibernate.api.Test.class); // 3. 根据配置生成表 SchemaExport schema = new SchemaExport(config); schema.create(true,true); // 4. 构建SessionFactory对象 SessionFactory factory = config.buildSessionFactory(); Session session = factory.openSession(); // 5. 建立连接 Transaction tran = session.beginTransaction(); // 6. 开启事务 Test t = new Test(); t.setName("test hibbernate"); session.save(t); tran.commit(); // 7.提交事务 session.close(); // 8.关闭会话 } |
Session 类的方法:
取得持久化对象的方法: get() load()
持久化对象都得保存,更新和删除:save(),update(),saveOrUpdate(),delete()
开启事务: beginTransaction().
管理Session 的方法:isOpen(),flush(), clear(), evict(),close()等
Transaction 类的方法:
commit():提交相关联的session实例
rollback():撤销事务操作
wasCommitted():检查事务是否提交
Hibernate4.0以后版本创建(安全线程)
public class HibernateSessionFactory { //指定Hibernate配置文件路径 private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml"; //创建ThreadLocal对象 private static final ThreadLocal<Session> sessionThreadLocal =new ThreadLocal<Session>(); //创建Configuration对象 private static Configuration configuration = new Configuration(); //定义SessionFactory对象 private static SessionFactory sessionFactory; //定义configFile属性并赋值 private static String configFile = CONFIG_FILE_LOCATION; static { try { //读取配置文件hibernate.cfg.xml configuration.configure(); //生成一个注册机对象 ServiceRegistry serviceRegistry=new ServiceRegistryBuilder(). applySettings(configuration.getProperties()).buildServiceRegistry();
//使用注册机对象serviceRegistry创建sessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (HibernateException e) { e.printStackTrace(); } }
//创建无参的HibernateSessionFactory构造方法 private HibernateSessionFactory(){ }
//获得SessionFactory对象 public static SessionFactory getSessionFactory() { return sessionFactory; }
//重建SessionFactory public static void rebuildSessionFactory() { synchronized (sessionFactory) { try { configuration.configure(configFile); ServiceRegistry serviceRegistry=new ServiceRegistryBuilder(). applySettings(configuration.getProperties()).buildServiceRegistry(); //使用注册机对象serviceRegistry创建sessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry); } catch (HibernateException e) { e.printStackTrace(); } } }
//获得Session对象 public static Session getSession() { //获得ThreadLocal对象管理的Session对象 Session session = (Session) sessionThreadLocal.get(); try { //判断Session对象是否已经存在或是否打开 if (session ==null || !session.isOpen()) { //如果Session对象为空或未打开,再判断sessionFactory对象是否为空 if (sessionFactory ==null) { //如果sessionFactory为空,则创建SessionFactory rebuildSessionFactory(); } //如果sessionFactory不为空,则打开Session session = (sessionFactory !=null) ? sessionFactory.openSession() :null; sessionThreadLocal.set(session); } } catch (HibernateException e) { e.printStackTrace(); } return session; }
//关闭Session对象 public static void closeSession() { Session session = (Session) sessionThreadLocal.get(); sessionThreadLocal.set(null); try { if (session !=null && session.isOpen()) { session.close(); } } catch (HibernateException e) { e.printStackTrace(); } }
//configFile属性的set方法 public static void setConfigFile(String configFile) { HibernateSessionFactory.configFile = configFile; sessionFactory = null; } //configuration属性的get方法 public static Configuration getConfiguration() { return configuration; } } | |
Session创建原理 由于SessionFactory是线程安全的,因而同一个SessionFactory实例可以被多个线程共 享,即多个并发线程可以同时访问一个SessionFactory并获得Session实例。但由于Sessior 不是线程安全的,如果多个并发线程同时操作同一个Session对象,就可能出现一个线程在 进行数据库操作,而另一个线程将Session对象关闭的情况,从而出现异常。如何才能保证 线程安全呢?这就要求SessionFactory能够针对不同的线程创建不同的Session对象,即需 要对Session进行有效的管理,Hibernate中使用ThreadLocal对象来维护和管理Session实例 ThreadLocal是指线程局部变量(Tread Local Variable),线程局部变量高效地为每个使用 它的线程提供单独的线程局部变量的副本。每个线程只能修改与自己相联系的副本,而不 会影响到其他进程的副本。为了实现为每个线程维护一个变量的副本, ThreadLocal类提供 了一个Map结构,其key值用来保存线程的ID, value值用来保存一个Session实例的副本。 这样,多个线程并发操作时,是在与自己绑定的Session实例副本上进行的,从而避免多个 线程在同一个Session实例上操作时可能导致的数据异常。 在 HibemateSessionFactory 类的 getSession()方法中,首先调用 ThreadLocal 类的 get()方法获得当前线程的Session对象,然后判断是否已存在该Session对象。如果该对象不存在或者该对象未打幵,再判断SessionFactory对象是否为空,如果SessionFactory对象不存在,先调用rebuildSessionFactory方法创建SessionFactory。如果存在则调用sessionFactory的openSession()方法创建Sessionsession对象。创建完成后还要调用threadLocal的set()方法为该线程保存session对象 |
持久化对象的状态
l 瞬时对象(Transient Objects)
使用new操作符初始化的对象不是立刻就持久化的,他们的状态是瞬时的。
(1) 不处于Session的缓存中,也可以说,不被任何一个Session实例关联。
(2) 在数据库中没有对应的记录。
l 持久化对象(Persist Objects)
持久实例是任何具有数据库标识的实例。它有持久化管理器Session统一管理,持久实例是在事务中进行操作的———他们的状态在事务结束时同数据库进行同步。
(1) 位于一个Session实例的缓存中,也可以说,持久化对象总是被一个Session实例关联。
(2) 持久化对象和数据库中的相关记录对应。
(3) Session在清理缓存时,会根据持久化对象的属性变化,来同步更新数据库。
l 离线对象(Detached Objects)
Session关闭之后,持久化对象就变为离线对象。离线表示这个对象不能再与数据库保持同步,他们不再受Hibernate管理。
(1) 不再位于Session的缓存中,也可以说,游离对象不被Session关联。
(2) 游离对象是由持久化对象转变过来的,因此在数据库中可能还存在与它对应的记录(前提条件是没有其他程序删除了这条记录)。
能够使hibernate的对象由瞬时态或托管态转变为持久态:
? save()方法:将对象由瞬时态转变为持久态。
? load()或get()方法:获得的对象的状态处于持久态。
? find()方法:获得的List集合中的对象状态处于持久态。
? update()、saveOrUpdate()和lock()方法:可将托管态对象转变为持久态。
能够使Hibernate的对象由持久态转变为托管态的方法:
? close()方法:调用后,Session的缓存会被清空,缓存中所有持久态对象状态都转变为托管态。处于托管状态的对象称为游离对象,当游离对象不再被引用时,将被Java虚拟机垃圾回收机制清除。
? evict()方法:可将Session缓存中一个指定的持久态对象删除,使其转变为托管态对象。当缓存中保存了大量处于持久态的对象时,为了节省内存空间,可以调用evict()方法删除一些持久态对象。
知识点
A. Hibernate的运行过程如下:
1、应用程序先调用Configuration类,该类读取Hibernate配置文件及映射文件中的信息,
2、并用这些信息生成一个SessionFactory对象,
3、然后从SessionFactory对象生成一个Session对象,
4、并用Session对象生成Transaction对象;
A、可通过Session对象的get(),load(),save(),update(),delete()
和saveOrUpdate()等方法对PO进行加载、保存、更新、删除、
等操作;
B、在查询的情况下,可通过Session对象生成一个Query对象,然后
利用Query对象执行查询操作;如果没有异常,Transaction对象将
提交这些操作到数据库中
B. session的get()和load()有什么区别?
get()如果没有找到持久化类返回null,有可能导致空指针异常。
load()如果没有找到持久化类直接抛出异常。
get()是直接加载数据,load()是延迟加载,当使用被加载数据的时候才发送SQL。
简而言之:Hibernate对于load()认为数据库一定存在,因此可以放心的使用代理进行延迟加载,如果使用中发现了问题,那么只能抛出异常。而对于get方法一定要获取真实的数据,否则返回null。
DataType dataType1 = (DataType) session.load(DataType.class,new Long(1));
DataType dataType2 = (DataType) session.load(DataType.class,new Long(1));
System.out.println(dataType1); // 延迟加载,需要使用才发送SQL语句
System.out.println(dataType2); // 从一级缓存中获取持久化对象
System.out.println(dataType1 == dataType2);
session.getTransaction().commit();
C. lazy 值
true 使用懒加载
false 关闭懒加载
extra (在集合数据懒加载时候提升效率)
在真正使用数据的时候才向数据库发送查询的sql;
如果调用集合的size()/isEmpty()方法,只是统计,不真正查询数据!
D. Query对象在获取表的所有的数据的时候,使用list()和 iterator()有什么
区别?
ü 编写代码的方式不同list()和iterator()
ü 底层发送的SQL语句不同
ü list()直接一次性获取到所有持久化类的对象,iterator()先获取的是所有的数据的id值。当真正的遍历使用数据的时候再发送select语句。因此该方法一定要处于session会话中。
ü list发送的查询语句只有1条。Iterator发送多条查询语句,因此iterator的效率低下。懒汉式(iterator) 饿汉式(list)
Hibernate Api
|-- Configuration 配置管理类对象
config.configure(); 加载主配置文件的方法(hibernate.cfg.xml)
默认加载src/hibernate.cfg.xml
config.configure(“cn/config/hibernate.cfg.xml”);加载指定路径下指定名称的主配 置文件
config.buildSessionFactory(); 创建session的工厂对象
|-- SessionFactory session的工厂(或者说代表了这个hibernate.cfg.xml配置文件)
sf.openSession(); 创建一个sesison对象
sf.getCurrentSession(); 创建session或取出session对象
|--Session session对象维护了一个连接(Connection), 代表了与数据库连接的会话。Hibernate最重要的对象: 只用使用hibernate与数据库操作,都用到这个对象
session.beginTransaction(); 开启一个事务; hibernate要求所有的与数据库的操作必须有事务的环境,否则报错!
更新:
session.save(obj); 保存一个对象
session.update(emp); 更新一个对象
session.saveOrUpdate(emp); 保存或者更新的方法:
à没有设置主键,执行保存;
à有设置主键,执行更新操作;
à如果设置主键不存在报错!
使用Session对象的save方法,虽然可以完成对象的持久化操作,但有时候会出现问题,如一个对象己经被持久化了,此时如果再次调用saveO方法,将会出现异常。 使用saveOrUpdateO方法可以很好地解决这一问题,因为它会自动判断该对象是否已经持久化,如果已经持久化,将执行更新操作,否则执行添加操作。如果标识(主键)的生成策略是自增型的,则使用Session对象的save()和saveOrUpdate〇方法是完全相同的。 提示: Session的save方法必须在事务环境中完成,并需使用commit方法提交事务,记录才能成功添加到数据表中。 |
主键查询:
session.get(Employee.class, 1); 主键查询
session.load(Employee.class, 1); 主键查询 (支持懒加载)
Hibernate技术(一)--Hibernate初步