HIbernate基础
HIbernate基本运用
1. 什么是hibernate?
Hibernate是一个持久化框架,主要用来做对象持久化的,保存数据知识
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库.Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。
2. 什么是对象持久化?对象持久化有什么用?(解决的问题)
对象持久化就是把内存中的对象永久的保存起来,保护对象的状态,方便使用
对象持久化有什么用: 1.解决掉电的问题 2.共享方便 3.保证对象安全 4.检索方便
3. ORM(对象关系映射)是什么?有什么作用?
ORM 就是对象关系映射。详细一点,是面向对象编程中的对象(Object)和关系数据库的
关系(Relation)的一个映射(Mapping)。
产生的原因是因为软件开发已经是面向对象的了。所有的值,操作都是基于对象的概念。
而数据库还是关系数据库。记录的存储还是一条一条的。为了在逻辑上使得软件开发与存取数据库保持一致。都按照对象的概念进行开发,存取,才出现了这种or-mapping
实际上,如果面向对象数据库成为主流,or-mapping就没用了。
好处:就是上面所属的逻辑上的一致性,开发人员不必管数据库怎么存储,
他只要知道要保存的也是一个对象(和他开发的概念一致)就可以了。
没有ORM之前,都是直接用SQL操作数据库,或者使用DAO之类的机制
进行对象概念到关系数据库概念的转换。
4. hibernate要素
POJO类:持久化对象,和数据库表进行对应
hibernate.cfg.xml:hibernate的核心配置文件,配置数据库连接信息,
Hibernate原数据,以及映射文件(*.hbm.xml)或者持久化类(annotation)
*.hbm.xml:将数据库表和pojo类进行一对应的配置文件
5 hibernate的编程步骤
java project--->add jars-->导入dtd(可略,验证,主要是实现帮助自动标识的功能)-->配置hibernate.cfg.xml---构建映射文件(将类和表建立映射关系)--- 测试类
Dtd导入,实现可联想;
window——preference--xml catalog--add---对jar包起名字--添加jar
*****以上是在pojo类中添加Annotation标注过程,注释具有自动联想功能***************
1.创建表(truncate截断表,清空)
2.构建projo类 设置属性和geter setter方法 无参和有参的构造方法
3.构建映射文件
4.测试对象
1)构建数据库表
create table employee( id number(7) constraint employee_id_pk primary key, name varchar2(10) constraint employee_name_nn not null, gender varchar2(10) constraint employee_gender_ck check(gender in('male’,’female'))); 表已创建。 //注意:gender in(‘male’,’female’) 不是gender in(‘male,female’); //这种都不会报错,只会当做gender in(‘ ’);一种 SQL> desc employee 名称 是否为空? 类型 ----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER(7) NAME NOT NULL VARCHAR2(10) GENDER VARCHAR2(10) SQL> create sequence employee_id start with 1 increment by 1; 序列已创建。 |
2)书写pojo类 实现序列化接口的类 Employee.java
package com.briup.hibernate.chap1; public class Employee { private int id; private String name; private String gender; public Employee() { super(); // TODO Auto-generated constructor stub } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + ", gender=" + gender + "]"; } public Employee(int id, String name, String gender) { super(); this.id = id; this.name = name; this.gender = gender; } 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 getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } } |
3)书写employee.hbm.xml
Employee.hbm.xml : <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD/EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- 注释标签 配置pojo文件和表之间的映射关系 class name 指定类名称--> <class name ="com.briup.hibernate.chap1.Employee" table="employee"> <!-- 配置主键关联关系 column表示表中的列 --> <id name="id" column ="id"></id> <!-- 配置一般属性关联关系 如果属性名称和列的名称相同可以省略--> <property name ="name"></property> <property name="gender"></property> </class> </hibernate-mapping> |
4)将映射文件或者pojo类加入到hibernate.cfg.xml中
Hibernate.cfg.xml文件:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="connection.driver_class"> oracle.jdbc.driver.OracleDriver </property> <property name="connection.url"> jdbc:oracle:thin:@127.0.0.1:1521:XE </property> <property name="connection.username"> briup </property> <property name="connection.password"> briup </property> <property name="connection.pool_size"> 1 </property> <property name="dialect"> org.hibernate.dialect.Oracle10gDialect </property> <property name="current_session_context_class"> thread </property> <property name="cache.provider_class"> org.hibernate.cache.NoCacheProvider </property> <property name="show_sql"> true </property> <!-- 配置文件 通过hibernate类构建表的作用--> <mapping resource ="com/briup/hibernate/chap1/employee.hbm.xml"/> </session-factory> </hibernate-configuration> |
6主键生成方式
hibernate提供的一级缓存
hibernate是一个线程对应一个session(负责增删改查,查询事务),一个线程可以看成一个用户。也就是说session级缓存(一级缓存)只能给一个线程用,别的线程用不了,一级缓存就是和线程绑定了。
hibernate一级缓存生命周期很短,和session生命周期一样,一级缓存也称session级的缓存或事务级缓存。如果tb事务提交或回滚了,我们称session就关闭了,生命周期结束了。缓存和连接池的区别:缓存和池都是放在内存里,实现是一样的,都是为了提高性能的。但有细微的差别,池是重量级的,里面的数据是一样的,比如一个池里放100个Connection连接对象,这个100个都是一样的。缓存里的数据,每个都不一样。比如读取100条数据库记录放到缓存里,这100条记录都不一样。
缓存主要是用于查询
Hibernate 二级缓存
二级缓存需要sessionFactory来管理,它是进初级的缓存,所有人都可以使用,它是共享的。二级缓存比较复杂,一般用第三方产品。hibernate提供了一个简单实现,用Hashtable做的,只能作为我们的测试使用,商用还是需要第三方产品。
使用缓存,肯定是长时间不改变的数据,如果经常变化的数据放到缓存里就没有太大意义了。因为经常变化,还是需要经常到数据库里查询,那就没有必要用缓存了。
hibernate做了一些优化,和一些第三方的缓存产品做了集成。老师采用EHCache缓存产品
/**
* API:
* 1.Configuration:这个类负责读取XML文档(映射配置文件)
* configure():读xml Configuration中的方法
* buildSessionFactory():创建一个生产session对象的工厂,其实是再次检查
SessionFactory factory = configuration.buildSessionFactory() 返回一个SessionFactory对象,可以产生Session对象 |
* 因为hibernate和jdbc不一样,jdbc是如果不手动设置开启事务,那它
* 就是马上执行sql的,hibernate的不会马上执行,是事务提交后执行
* 默认情况下就是打开事务的状态,这里只是再检查以下
* 2.SessionFactory:负责生产session对象
* openSession():创建一个session
Session session = factory.openSession(); 用SessionFactory中的openSession方法创建Session对象,Session类:这个是主要的类,负责增删改查,开启事务 |
* 3.Session类:这个是主要的类,负责增删改查,开启事务等
* beginTransaction():产生一个事务对象(Transaction)
Transaction transaction = session.beginTransaction(); Session中的beginTransaction()方法返回Transaction对象,用于管理事务 |
* save():增加相当于操作sql中的insert语句
Employee employee = new Employee();//设置Employee里面的对象 employee.setId(2); employee.setName("tom"); employee.setGender("male") |
session.save(employee); // 将employee对象插入 transaction.commit(); //提交 session.close();//关闭资源 |
* 4.Transaction类:负责管理事务的
* commit():提交一个事务
*/
package com.briup.hibernate.chap1; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.classic.Session; public class EmployeeTest { public static void main(String[] args) { // 构建configuration用来读取hibernate.cfg.xml文件 Configuration configuration = new Configuration(); // 启动configuration方法 configuration.configure(); // 构建SeeionFActory工厂,用来创建session对象 SessionFactory factory = configuration.buildSessionFactory(); // 构建session对象用来创建transaction对象 Session session = factory.openSession(); // 创建事务对象hibernate中事务并没有自动开启 // jdbc中的事务是自动开启的所以每次执行sql语句都不需要执行commit操作 // hibernate需要执行commit操作,数据才能够真正被写入到数据库中,所以要构建 // 事务,用户调用事务,手动进行commit操作 Transaction transaction = session.beginTransaction(); Employee employee = new Employee(); employee.setId(2); employee.setName("tom"); employee.setGender("male"); // 通过save方法将Employee对象传给session对象 session.save(employee); transaction.commit(); session.close(); } } |
1).assigned:
主键由外部程序负责生成,无需Hibernate参与。----如果要由程序代码来指定主键,就采有这种
<id name="id" column="id" type="string">
<generator class="assigned" />
</id>
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD/EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- 注释标签 配置pojo文件和表之间的映射关系 class name 指定类名称 --> <class name ="com.briup.hibernate.chap1.Employee" table="employee"> <!-- 配置主键关联关系 column表示表中的列 --> <id name="id" column ="id"></id> <!-- 配置一般属性关联关系 如果属性名称和列的名称相同可以省略--> <property name ="name"></property> <property name="gender"></property> </class> </hibernate-mapping> |
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD/EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name ="com.briup.hibernate.chap1.Employee" table="employee"> <id name="id" column ="id"> <generator class="assigned"></generator></id> <!-- 配置一般属性关联关系 如果属性名称和列的名称相同可以省略--> <property name ="name"></property> <property name="gender"></property> </class> </hibernate-mapping> |
<generator class="increment"></generator> 不需要手动添加id,写的id还是按照原来的顺序执行 increment会产生两条sql语句,如果手动赋值无效 |
employee.setId(10); //语句无效 不是10 employee.setName("tom"); session.save(employee); |
ID NAME GENDER ----- -------------------- ---------- 1 tom male 2 tom male 3 tom male |
SQL> drop sequence employee_id;
序列已删除。 SQL> create sequence employee_id 2 start with 10 3 increment by 2;
序列已创建。 |
<generator class="sequence"> <param name="sequence">employee_id</param> </generator> |
employee.setGender("male"); employee.setName("tom"); ---------- -------------------- --------- 1 tom male 2 tom male 3 tom male 10 tom male 12 tom male 14 tom male 以2增加 |
Sequence会产生两个sql语句(select序列的下一个值,insert) 如果手动赋值id无效 |
Hilo只会产生一条sql语句效率高, id值由hilo(高位值)*max_lo+lo(低位值)构成 |
<generator class="hilo"> <param name="table">hibernate_hilo</param> <param name="colunm">next_id</param> <param name="max_lo">100</param> </generator> |
<generator class="uuid"></generator> |
SQL> select * from employee;
ID ------------------------------------------ NAME GENDER -------------------- -------------------- 4028f081547bbb1101547bbb12190000 tom male
4028f081547bbcfa01547bbcfb670000 tom male
|
create table wife( id number(7) constraint wife_id_pk primary key, name varchar2(10) constraint wife_name_nn not null, hus_id number(7) constraint wife_hus_id_fk references husband(id)); |
2).increment:
对 long 、short 或 int 的数据列生成自动增长主键。increment主键生成方式的特点是与底层数据库无关性,大部分数据库如 Mysql,MSSQL 和ORACLE等都支持increament生成方式。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。increment方式的不足之处是当多个线程并发对数据库表进行写操作时,可能出现相同的主键值,发生主键重复的冲突,因此多线程并发操作时,不应该使用此方法。
<id name="id" column="id">
<generator class="increment" />
</id>
3).identity:
如果数据列的类型是 long, short 或 int ,可使用主键生成器生成自动增长Hibernate主键。
与底层数据库有关,要求数据库支持identify,如MySQL中是auto_increment,SQL Server中是Identify.
支持的数据库有MySQL,SQL Server,DB2,Sybase和HypersonicSQL.(好像不支持oracle) 无需Hibernate和用户的干涉,
使用较为方便,但不便于在不同的数据库之间移植程序。identity的优点是不会发生 increment方式的并发错做问题。
数据库涉及到的表要设置自动增长。
<id name="id" column="id" type="long">
<generator class="identity" />
</id>
4)sequence:
在ORACLE等数据库中使用sequence生成主键。sequence的特点是于数据库的相关性,seqhio要求底层能支持sequence,例如Oracle。
数据库中的语法如下:
Oracle:
create sequence seq_name
increment by 1
start with 1;
需要主键值时可以调用seq_name.nextval或者seq_name.curval得到,数据库会帮助我们维护这个sequence序列,保证每次取到的值唯一,如:
insert into tbl_name(id, name) values(seq_name.nextval, ‘terry ren’);
<id name="id" column="id" type="long">
<generator class="sequence">
<param name="sequence">seq_name</param>
</generator>
</id>
5)hilo:
HIbernate主键详解------Hilo
Hilo使用高低位算法生成主键,高低位算法使用一个高位值和一个低位值,然后把算法得到的两个值拼接起来作为数据库中的唯一主键。Hilo方式需要额外的数据库表和字段提供高位值来源。默认请况下使用的表是通过hi/lo 算法实现的主键生成机制,需要额外的数据库表保存主键生成历史状态。hibernate_unique_key,默认字段叫作next_hi。 next_hi必须有一条记录否则会出现错误。
特点:需要额外的数据库表的支持,能保证同一个数据库中主键的唯一性,但不能保证多个数据库之间主键的唯一性。Hilo主键生成方式由Hibernate维护,所以Hilo方式与底层数据库无关,但不应该手动修改hi/lo算法使用的表的值,否则会引起主键重复的异常。
create table hi_value(next_hi number not null);
insert into hi_value(next_hi) values(1);
commit;
<id name="id" column="id">
<generator class="hilo">
<param name="table">hi_value</param>
<param name="column">next_hi</param>
<param name="max_lo">100</param>
</generator>
</id>
6)seqhio:
与hilo 类似,通过hi/lo 算法实现的主键生成机制,只是主键历史状态保存在Sequence中,适用于支持Sequence的数据库,
如Oracle。如果数据列的类型是 long, short 或 int可使用该主键生成器。
<id name="id" column="id">
<generator class="seqhilo">
<param name="sequence">seq_name</param>
<param name="max_lo">100</param>
</generator>
</id>
7)native:
由Hibernate根据不同的数据库方言,自行判断采用identity、hilo、sequence其中一种作为Hibernate主键生成方式,
native的 优点是与底层性无关,便于不同数据库之间的移植,由Hibernate根据不同数据库选择主键的生成方式。
在oracle中需要创建叫 Hibernate_sequence名字的sequence,如果设置了Hibernate.hbm2ddl.auto属性,
不需要手动建立序列,前提 是数据库帐号必须有Create Sequence这种高级权限。mysql等数据库则不用建立sequence。
<id name="id" column="id">
<generator class="native" />
</id>
8)uuid.hex:
采用基于128位的算法生成唯一值,并编制成32位长度的唯一字符串作为主键值,uuid.hex的优点是支持大部分数据库,
缺点就是要占用较大的存储空间。对于并发Insert要求较高的系统,推荐采用uuid.hex 作为主键生成机制。
<id name="id" column="id">
<generator class="uuid.hex" />
</id>
9)uuid.string:
使用UUID算法,UUID被编码为一个16个字符长的任意ASCII字符组成的字符串。不能在PostgreSQL数据库中使用。uuid.string同uuid.hex类似,
需要占很大的存储空间。
10)foreign:
使用外部表的字段作为Hibernate主键。
一般而言,利用uuid.hex方式生成Hibernate主键将提供最好的性能和数据库平台适应性。
11)select
使用触发器生成主键(主要用于早期的数据库主键生成机制,少用)
另外由于常用的数据库,如Oracle、DB2、 SQLServer、MySql 等,都提供了易用的主键生成机制(Auto-Increase 字段或者Sequence)。
我们可以在数据库提供的主键生成机制上采用generator-class=native的主键生成方式。不过值得注意的是,一些数据库提供的主键生成机制在效率上未必最佳,大量并发insert数据时可能会引起表之间的互锁。数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状态(如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量),之后每次插入数据会读取这个最大值,然后加上递增量作为新记 录的主键,之后再把这个新的最大值更新回内部表中,这样,一次Insert操作可能导致数据库内部多次表读写操作,同时伴随的还有数据的加锁解锁操作,这对性能产生了较大影响。因此,对于并发Insert要求较高的系统,推荐采用uuid作为主键生成机制。
7,hibernate tags
<hibernate-mapping
schema="schemaName" (1)
catalog="catalogName" (2)
default-cascade="cascade_style" (3)
default-access="field|property|ClassName" (4)
default-lazy="true|false" (5)
auto-import="true|false" (6)
package="package.name" (7)
/>
(1) schema (可选): 数据库schema的名称。
(2) catalog (可选): 数据库catalog的名称。
(3) default-cascade (可选 - 默认为 none): 默认的级联风格。
(4) default-access (可选 - 默认为 property): Hibernate用来访问所有属性的策略。可以通过实现PropertyAccessor接口 自定义。
(5) default-lazy (可选 - 默认为 true): 指定了未明确注明lazy属性的Java属性和集合类, Hibernate会采取什么样的默认加载风格。
(6) auto-import (可选 - 默认为 true): 指定我们是否可以在查询语言中使用非全限定的类名(仅限于本映射文件中的类)。
(7) package (可选): 指定一个包前缀,如果在映射文档中没有指定全限定的类名, 就使用这个作为包名。
假若你有两个持久化类,它们的非全限定名是一样的(就是两个类的名字一样,所在的包不一样--译者注),
你应该设置auto-import="false"。如果你把一个“import过”的名字同时对应两个类, Hibernate会抛出一个异常。
==================
<class
name="ClassName" (1)
table="tableName" (2)
discriminator-value="discriminator_value" (3)
mutable="true|false" (4)
schema="owner" (5)
catalog="catalog" (6)
proxy="ProxyInterface" (7)
dynamic-update="true|false" (8)
dynamic-insert="true|false" (9)
select-before-update="true|false" (10)
polymorphism="implicit|explicit" (11)
where="arbitrary sql where condition" (12)
persister="PersisterClass" (13)
batch-size="N" (14)
optimistic-lock="none|version|dirty|all" (15)
lazy="true|false" (16)
entity-name="EntityName" (17)
check="arbitrary sql check condition" (18)
rowid="rowid" (19)
subselect="SQL expression" (20)
abstract="true|false" (21)
node="element-name"
/>
(1) name (可选): 持久化类(或者接口)的Java全限定名。 如果这个属性不存在,Hibernate将假定这是一个非POJO的实体映射。
(2) table (可选 - 默认是类的非全限定名): 对应的数据库表名,生成DDL时数据表名,如果省略,则名称同持久化类名称。
(3) discriminator-value (可选 - 默认和类名一样): 一个用于区分不同的子类的值,在多态行为时使用.它可以接受的值包括 null 和 not null。
(4) mutable (可选,默认值为true): 表明该类的实例是可变的或者不可变的。
(5) schema (可选): 覆盖在根<hibernate-mapping>元素中指定的schema名字。
(6) catalog (可选): 覆盖在根<hibernate-mapping>元素中指定的catalog名字。
(7) proxy (可选): 指定一个接口,在延迟装载时作为代理使用。 你可以在这里使用该类自己的名字。
(8) dynamic-update (可选, 默认为 false): 指定用于UPDATE 的SQL将会在运行时动态生成,并且只更新那些改变过的字段
(只更新修改的字段,没有修改的字段不进行更新)。
(9) dynamic-insert (可选, 默认为 false): 指定用于INSERT的 SQL 将会在运行时动态生成,并且只包含那些非空值字段
(在添加记录时,只添加非null的字段)。
(10) select-before-update (可选, 默认为 false): 指定Hibernate除非确定对象真正被修改了(如果该值为true-译注),
否则不会执行SQL UPDATE操作。在特定场合(实际上,它只在一个瞬时对象(transient object)关联到一个 新的session中时
执行的update()中生效),这说明Hibernate会在UPDATE 之前执行一次额外的SQL SELECT操作,来决定是否应该执行 UPDATE。
(11) polymorphism(多态) (可选, 默认值为 implicit (隐式) ): 界定是隐式还是显式的使用多态查询
(这只在Hibernate的具体表继承策略中用到-译注)。
(12) where (可选) 指定一个附加的SQLWHERE 条件, 在抓取这个类的对象时会一直增加这个条件。
(13) persister (可选): 指定一个定制的ClassPersister。
(14) batch-size (可选,默认是1) 指定一个用于 根据标识符(identifier)抓取实例时使用的"batch size"(批次抓取数量)。
(15) optimistic-lock(乐观锁定) (可选,默认是version): 决定乐观锁定的策略。
(16) lazy (可选): 通过设置lazy="false", 所有的延迟加载(Lazy fetching)功能将被全部禁用(disabled)。
(17) entity-name (可选,默认为类名): Hibernate3允许一个类进行多次映射( 前提是映射到不同的表),并且允许使用Maps或XML代替Java层次的实体映射 (也就是实现动态领域模型,不用写持久化类-译注)。 (18) check (可选): 这是一个SQL表达式, 用于为自动生成的schema添加多行(multi-row)约束检查。
(19) rowid (可选): Hibernate可以使用数据库支持的所谓的ROWIDs,例如: Oracle数据库,如果你设置这个可选的rowid, Hibernate可以使用额外的字段rowid实现快速更新。ROWID是这个功能实现的重点, 它代表了一个存储元组(tuple)的物理位置。
(20) subselect (可选): 它将一个不可变(immutable)并且只读的实体映射到一个数据库的 子查询中。当你想用视图代替一张基本表的时候,这是有用的,但最好不要这样做。更多的介绍请看下面内容。
(21)abstract (可选): 用于在<union-subclass>的继承结构 (hierarchies)中标识抽象超类。
若指明的持久化类实际上是一个接口,这也是完全可以接受的。 之后你可以用元素<subclass>来指定该接口的实际实现类。
你可以持久化任何static(静态的)内部类。 你应该使用标准的类名格式来指定类名,比如:Foo$Bar。
不可变类,mutable="false"不可以被应用程序更新或者删除。 这可以让Hibernate做一些小小的性能优化。
可选的proxy属性允许延迟加载类的持久化实例。 Hibernate开始会返回实现了这个命名接口的CGLIB代理。当代理的某个方法被实际调用的时候,真实的持久化对象才会被装载。参见下面的“用于延迟装载的代理”。
Implicit (隐式)的多态是指,如果查询时给出的是任何超类、该类实现的接口或者该类的 名字,都会返回这个类的实例;
如果查询中给出的是子类的名字,则会返回子类的实例。 Explicit (显式)的多态是指,只有在查询时给出明确的该类名字时才会
返回这个类的实例; 同时只有在这个<class>的定义中作为<subclass> 或者<joined-subclass>出现的子类,才会可能返回。
在大多数情况下,默认的polymorphism="implicit"都是合适的。 显式的多态在有两个不同的类映射到同一个表的时候很有用。
(允许一个“轻型”的类,只包含部分表字段)。
persister属性可以让你定制这个类使用的持久化策略。 你可以指定你自己实现 org.hibernate.persister.EntityPersister的子类,
你甚至可以完全从头开始编写一个 org.hibernate.persister.ClassPersister接口的实现, 比如是用储存过程调用、序列化到文件
或者LDAP数据库来实现。 参阅org.hibernate.test.CustomPersister,这是一个简单的例子 (“持久化”到一个Hashtable)。
请注意dynamic-update和dynamic-insert的设置并不会继承到子类, 所以在<subclass>或者<joined-subclass>元素中可能 需要再次设置。
这些设置是否能够提高效率要视情形而定。请用你的智慧决定是否使用。
使用select-before-update通常会降低性能。如果你重新连接一个脱管(detache)对象实例 到一个Session中时,
它可以防止数据库不必要的触发update。 这就很有用了。
如果你打开了dynamic-update,你可以选择几种乐观锁定的策略:
version(版本检查) 检查version/timestamp字段
all(全部) 检查全部字段
dirty(脏检查)只检察修改过的字段
none(不检查)不使用乐观锁定
我们非常强烈建议你在Hibernate中使用version/timestamp字段来进行乐观锁定。 对性能来说,这是最好的选择,
并且这也是唯一能够处理在session外进行操作的策略(例如: 在使用Session.merge()的时候)。
对Hibernate映射来说视图和表是没有区别的,这是因为它们在数据层都是透明的( 注意:一些数据库不支持视图属性,特别是更新的时候)。
有时你想使用视图,但却不能在数据库 中创建它(例如:在遗留的schema中)。这样的话,你可以映射一个不可变的(immutable)
并且是 只读的实体到一个给定的SQL子查询表达式:
java代码:
<class name="Summary">
<subselect>
select item.name, max(bid.amount), count(*)
from item
join bid on bid.item_id = item.id
group by item.name
</subselect>
<synchronize table="item"/>
<synchronize table="bid"/>
<id name="name"/>
...
</class>
定义这个实体用到的表为同步(synchronize),确保自动刷新(auto-flush)正确执行, 并且依赖原实体的查询不会返回过期数据。<subselect>在属性元素 和一个嵌套映射元素中都可见。
=========================
<id
name="propertyName"
type="typename"
column="column_name"
unsaved-value="any|none|null|id_value"
access="field|property|ClassName">
<generator class="generatorClass"/>
</id>
name (可选): 标识属性的名字。
type (可选): 标识Hibernate类型的名字。
column (可选 - 默认为属性名): 主键字段的名字。
unsaved-value (可选 - 默认为null): 一个特定的标识属性值,用来标志该实例是刚刚创建的,尚未保存。这可以把这种实例和从以前的session中装载过(可能又做过修改--译者注)但未再次持久化的实例区分开来。
access (可选 - 默认为property): Hibernate用来访问属性值的策略。
如果 name属性不存在,会认为这个类没有标识属性。
unsaved-value 属性很重要!如果你的类的标识属性不是默认为null的,你应该指定正确的默认值。
还有一个另外的<composite-id>声明可以访问旧式的多主键数据。我们强烈不鼓励使用这种方式。
1. generator
必须声明的<generator>子元素是一个Java类的名字,用来为该持久化类的实例生成唯一的标识。如果这个生成器实例需要某些配置值或者初始化参数,用<param>元素来传递。
<id name="id" type="long" column="uid" unsaved-value="0">
<generator class="net.sf.hibernate.id.TableHiLoGenerator">
<param name="table">uid_table</param>
<param name="column">next_hi_value_column</param>
</generator>
</id>
所有的生成器都实现net.sf.hibernate.id.IdentifierGenerator接口。这是一个非常简单的接口;某些应用程序可以选择提供他们自己特定的实现。当然,Hibernate提供了很多内置的实现。下面是一些内置生成器的快捷名字:
increment(递增)
用于为long, short或者int类型生成唯一标识。只有在没有其他进程往同一张表中插入数据时才能使用。 在集群下不要使用。
identity
对DB2,MySQL, MS SQL Server, Sybase和HypersonicSQL的内置标识字段提供支持。返回的标识符是long, short 或者int类型的。
sequence (序列)
在DB2,PostgreSQL, Oracle, SAP DB, McKoi中使用序列(sequence),而在Interbase中使用生成器(generator)。返回的标识符是long, short或者 int类型的。
hilo (高低位)
使用一个高/低位算法来高效的生成long, short或者 int类型的标识符。给定一个表和字段(默认分别是是hibernate_unique_key 和next)作为高位值得来源。高/低位算法生成的标识符只在一个特定的数据库中是唯一的。在使用JTA获得的连接或者用户自行提供的连接中,不要使用这种生成器。
seqhilo(使用序列的高低位)
使用一个高/低位算法来高效的生成long, short或者 int类型的标识符,给定一个数据库序列(sequence)的名字。
uuid.hex
用一个128-bit的UUID算法生成字符串类型的标识符。在一个网络中唯一(使用了IP地址)。UUID被编码为一个32位16进制数字的字符串。
uuid.string
使用同样的UUID算法。UUID被编码为一个16个字符长的任意ASCII字符组成的字符串。不能使用在PostgreSQL数据库中
native(本地)
根据底层数据库的能力选择identity, sequence 或者hilo中的一个。
assigned(程序设置)
让应用程序在save()之前为对象分配一个标示符。
foreign(外部引用)
使用另外一个相关联的对象的标识符。和<one-to-one>联合一起使用。
2. 高/低位算法(Hi/Lo Algorithm)
hilo 和 seqhilo生成器给出了两种hi/lo算法的实现,这是一种很令人满意的标识符生成算法。第一种实现需要一个“特殊”的数据库表来保存下一个可用的“hi”值。第二种实现使用一个Oracle风格的序列(在被支持的情况下)。
<id name="id" type="long" column="cat_id">
<generator class="hilo">
<param name="table">hi_value</param>
<param name="column">next_value</param>
<param name="max_lo">100</param>
</generator>
</id>
<id name="id" type="long" column="cat_id">
<generator class="seqhilo">
<param name="sequence">hi_value</param>
<param name="max_lo">100</param>
</generator>
</id>
很不幸,你在为Hibernate自行提供Connection,或者Hibernate使用JTA获取应用服务器的数据源连接的时候无法使用hilo 。Hibernate必须能够在一个新的事务中得到一个"hi"值。在EJB环境中实现hi/lo算法的标准方法是使用一个无状态的session bean。
3. UUID算法(UUID Algorithm )
UUID包含:IP地址,JVM的启动时间(精确到1/4秒),系统时间和一个计数器值(在JVM中唯一)。在Java代码中不可能获得MAC地址或者内存地址,所以这已经是我们在不使用JNI的前提下的能做的最好实现了。
不要试图在PostgreSQL中使用uuid.string。
4. 标识字段和序列(Identity Columns and Sequences)
对于内部支持标识字段的数据库(DB2,MySQL,Sybase,MS SQL),你可以使用identity关键字生成。对于内部支持序列的数据库(DB2,Oracle, PostgreSQL, Interbase, McKoi,SAP DB),你可以使用sequence风格的关键字生成。这两种方式对于插入一个新的对象都需要两次SQL查询。
<id name="id" type="long" column="uid">
<generator class="sequence">
<param name="sequence">uid_sequence</param>
</generator>
</id>
<id name="id" type="long" column="uid" unsaved-value="0">
<generator class="identity"/>
</id>
对于跨平台开发,native策略会从identity, sequence 和hilo中进行选择,取决于底层数据库的支持能力。
下面再给个简单例子:
<id name="id" type="long" column="uid">
<generator class="sequence">
<param name="sequence">uid_sequence</param>
</genarator>
</id>
注意:
<generator class=""> 里面可以选啊!推荐Oracle用sequence,seqhilo
================
<property>元素为类定义了一个持久化的,JavaBean风格的属性。
<property
name="propertyName" (1)
column="column_name" (2)
type="typename" (3)
update="true|false" (4)
insert="true|false" (4)
formula="arbitrary SQL expression" (5)
access="field|property|ClassName" (6)
lazy="true|false" (7)
unique="true|false" (8)
not-null="true|false" (9)
optimistic-lock="true|false" (10)
generated="never|insert|always" (11)
node="element-name|@attribute-name|element/@attribute|."
index="index_name"
unique_key="unique_key_id"
length="L"
precision="P"
scale="S"
/>
(1) name: 实体类属性的名字,以小写字母开头。
(2) column (可选 - 默认为属性名字): 对应的数据库字段名。 也可以通过嵌套的<column>元素指定。(如果省略则使用,则使用name所指定的名称为字段名)
(3) type (可选): 一个Hibernate类型的名字(省略则使用hibernate默认类型),也可以自己配置其它hbernate类型(integer, long, short, float, double, character, byte, boolean, yes_no, true_false)。
(4) update, insert (可选 - 默认为 true) : 表明用于UPDATE 和/或 INSERT 的SQL语句中是否包含这个被映射了的字段。这二者如果都设置为false 则表明这是一个“外源性(derived)”的属性,它的值来源于映射到同一个(或多个) 字段的某些其他属性,或者通过一个trigger(触发器)或其他程序生成。
(5) formula (可选): 一个SQL表达式,定义了这个计算 (computed) 属性的值。计算属性没有和它对应的数据库字段。
(6) access (可选 - 默认值为 property): Hibernate用来访问属性值的策略。
(7) lazy (可选 - 默认为 false): 指定 指定实例变量第一次被访问时,这个属性是否延迟抓取(fetched lazily)( 需要运行时字节码增强)。
(8) unique (可选): 使用DDL为该字段添加唯一的约束。 同样,允许它作为property-ref引用的目标。
(9) not-null (可选): 使用DDL为该字段添加可否为空(nullability)的约束。
(9) length(可选):当type为varchar时,设置字段长度
(10) optimistic-lock (可选 - 默认为 true): 指定这个属性在做更新时是否需要获得乐观锁定(optimistic lock)。 换句话说,它决定这个属性发生脏数据时版本(version)的值是否增长。
(11) generated (可选 - 默认为 never): 表明此属性值是否实际上是由数据库生成的。
typename可以是如下几种:
Hibernate基本类型名(比如:integer, string, character,date, timestamp, float, binary, serializable, object, blob)。
一个Java类的名字,这个类属于一种默认基础类型 (比如: int, float,char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob)。
一个可以序列化的Java类的名字。
一个自定义类型的类的名字。(比如: com.illflow.type.MyCustomType)。
如果你没有指定类型,Hibernarte会使用反射来得到这个名字的属性,以此来猜测正确的Hibernate类型。 Hibernate会按照规则2,3,4的顺序对属性读取器(getter方法)的返回类进行解释。然而,这还不够。 在某些情况下你仍然需要type属性。(比如,为了区别Hibernate.DATE 和Hibernate.TIMESTAMP,或者为了指定一个自定义类型。)
access属性用来让你控制Hibernate如何在运行时访问属性。在默认情况下, Hibernate会使用属性的get/set方法对(pair)。如果你指明access="field", Hibernate会忽略get/set方法对,直接使用反射来访问成员变量。你也可以指定你自己的策略, 这就需要你自己实现org.hibernate.property.PropertyAccessor接口, 再在access中设置你自定义策略类的名字。
衍生属性(derive propertie)是一个特别强大的特征。这些属性应该定义为只读,属性值在装载时计算生成。 你用一个SQL表达式生成计算的结果,它会在这个实例转载时翻译成一个SQL查询的SELECT 子查询语句。
java代码:
<property name="totalPrice"
formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
WHERE li.productId = p.productId
AND li.customerId = customerId
AND li.orderNumber = orderNumber )"/>
注意,你可以使用实体自己的表,而不用为这个特别的列定义别名( 上面例子中的customerId)。同时注意,如果你不喜欢使用属性, 你可以使用嵌套的<formula>映射元素。
注意:如果实体类和实体类中的属性和SQL中的关键重复,必须采用table或column重新命名。