- 浏览: 83936 次
最新评论
-
h416373073:
内容不多
properties几点注意事项 -
编程的石头:
COPY过去了,呵呵,偷点懒,没看懂的可以去看下这篇文章,原理 ...
Log4j多个日志文件 -
tianwaishan:
路过,,,,
properties几点注意事项 -
rich3385:
好贴,有帮助,如果能深入详细一些就更好了。
Birt初探 -
spacefly:
顶 ssss
利用开源组件制作验证码
Hibernate 闲话杂记
解压缩从Hibernate网站http://www.hibernate.org下载的Hibernate发布包,并把/lib目录下所有需要的库文件拷到新建开发目录下的/lib目录下。
想使用Hibernate,以下是运行时所需要的最小库文件集合:
antlr.jar
cglib.jar
asm.jar
asm-attrs.jars
commons-collections.jar
commons-logging.jar
ehcache.jar
hibernate3.jar
jta.jar
dom4j.jar
log4j.jar
配置Hibernate
<?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>
<!-- Database connection settings -->
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- 将显示的SQL排版,方便观看 -->
<property name="format_sql">true</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<mapping resource="events/Event.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Hibernate日志类别
类别 功能
org.hibernate.SQL 在所有SQL DML语句被执行时为它们记录日志
org.hibernate.type 为所有JDBC参数记录日志
org.hibernate.tool.hbm2ddl 在所有SQL DDL语句执行时为它们记录日志
org.hibernate.pretty 在session清洗(flush)时,为所有与其关联的实体(最多20个)的状态记录日志
org.hibernate.cache 为所有二级缓存的活动记录日志
org.hibernate.transaction 为事务相关的活动记录日志
org.hibernate.jdbc 为所有JDBC资源的获取记录日志
org.hibernate.hql.AST 在解析查询的时候,记录HQL和SQL的AST分析日志
org.hibernate.secure 为JAAS认证请求做日志
org.hibernate 为任何Hibernate相关信息做日志 (信息量较大, 但对查错非常有帮助)
class元素来定义一个持久化类:
<class
name="ClassName"
table="tableName"
discriminator-value="discriminator_value"
mutable="true|false"
schema="owner"
catalog="catalog"
proxy="ProxyInterface"
dynamic-update="true|false"
dynamic-insert="true|false"
select-before-update="true|false"
polymorphism="implicit|explicit"
where="arbitrary sql where condition"
persister="PersisterClass"
batch-size="N"
optimistic-lock="none|version|dirty|all"
lazy="true|false"
entity-name="EntityName"
check="arbitrary sql check condition"
rowid="rowid"
subselect="SQL expression"
abstract="true|false"
node="element-name"
/>
1. name (可选): 持久化类(或者接口)的Java全限定名。 如果这个属性不存在,Hibernate将假定这是一个非POJO的实体映射。
2. table (可选 - 默认是类的非全限定名): 对应的数据库表名。
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 将会在运行时动态生成,并且只包含那些非空值字段。
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)中标识抽象超类。
强烈建议在Hibernate中使用version/timestamp字段来进行乐观锁定。 对性能来说,这是最好的选择,并且这也是唯一能够处理在session外进行操作的策略(例如: 在使用Session.merge()的时候)。
UUID包含:IP地址,JVM的启动时间(精确到1/4秒),系统时间和一个计数器值(在JVM中唯一)。 在Java代码中不可能获得MAC地址或者内存地址,所以这已经是在不使用JNI的前提下的能做的最好实现了。
Hibernate要求持久化集合值字段必须声明为接口
为了能在应用程序服务器(application server)中使用Hibernate, 应当总是将Hibernate 配置成从注册在JNDI中的Datasource处获得连接,至少需要设置下列属性中的一个.
这是一个使用应用程序服务器提供的JNDI数据源的hibernate.properties样例文件:
hibernate.connection.datasource = java:/comp/env/jdbc/test
hibernate.transaction.factory_class = \org.hibernate.transaction.JTATransactionFactory
hibernate.transaction.manager_lookup_class = \org.hibernate.transaction.JBossTransactionManagerLookup
hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
Hibernate自带的连接池算法相当不成熟. 并不适合用于产品系统或性能测试中。 出于最佳性能和稳定性考虑应该使用第三方的连接池。只需要用特定连接池的设置替换 hibernate.connection.pool_size即可。可以使用C3P0连接池:
<!-- C3P0 连接池设定 -->
<!-- 最小的Connection数目 -->
<property name="c3p0.min_size">5</property>
<!-- 最大的Connection数目 -->
<property name="c3p0.max_size">20</property>
<!-- 允许的idle时间 -->
<property name="c3p0.timeout">300</property>
<!-- 最大的Statement数目 -->
<property name="c3p0.max_statements">50</property>
<!-- idle的测试周期 -->
<property name="c3p0.idle_test_period">3000</property>
使用Tomcat的话,也可以透过它提供的DBCP连接池来取得连接。
设定好容器提供的DBCP连接池之后,只要在配置文件中加入connection.datasource属性,例如在 hibernate.cfg.xml中加入:
<property name="connection.datasource">java:comp/env/jdbc/dbname</property>
如果是在hibernate.properties中的话,则加入:
hibernate.connection.datasource = java:comp/env/jdbc/dbname
如果有个属性,实际上并没有与之对应,例如使用COUNT('filed')来取得,则可以使用formula属性,例如:
...
<property name="average" formula="(SELECT AVG(u.age) FROM T_USER u)"/>
...
设定< id>标签的unsaved-value来决定什么是新的值必需,什么是已有的值必须更新:
<id name="id" column="id" type="java.lang.Integer" unsaved-value="null">
<generator class="native"/>
</id>
unsaved-value可以设定的值包括:
any:总是储存
none:总是更新
null:id为null时储存(预设)
valid:id为null或是指定值时储存
这样设定之后,可以使用Session的saveOrUpdate()方法来取代update()方法。
可编程的配置方式
一个org.hibernate.cfg.Configuration实例代表了一个应用程序中Java类型 到SQL数据库映射的完整集合. Configuration被用来构建一个(不可变的 (immutable))SessionFactory. 映射定义则由不同的XML映射定义文件编译而来.
可以直接实例化Configuration来获取一个实例,并为它指定XML映射定义 文件. 如果映射定 义文件在类路径(classpath)中, 请使用addResource():
Configuration cfg = new Configuration()
.addResource("Item.hbm.xml")
.addResource("Bid.hbm.xml");
一个替代方法(有时是更好的选择)是,指定被映射的类,让Hibernate帮寻找映射定义文件:
Configuration cfg = new Configuration()
.addClass(org.hibernate.auction.Item.class)
.addClass(org.hibernate.auction.Bid.class);
Hibernate将会在类路径(classpath)中寻找名字为 /org/hibernate/auction/Item.hbm.xml和 /org/hibernate/auction/Bid.hbm.xml映射定义文件. 这种方式消除了任何对文件名的硬编码(hardcoded).
从数据库加载数据:
Session 有三种查询方法get(),load(),find(),前两种都是根据对象ID加载对象,后者类似HQL查询一个或多个对象.
Hibernate要求复合主键类别要实作Serializable介面,并定义equals()与hashCode()方法.
注意如果没有匹配的数据库记录,load()方法可能抛出无法恢复的异常(unrecoverable exception)。如果不确定是否有匹配的行存在,应该使用get()方法,它会立刻访问数据库,如果没有对应的记录,会返回null。如果不知道所要寻找的对象的持久化标识,那么需要使用查询。
一个查询通常在调用list()时被执行,执行结果会完全装载进内存中的一个集合(collection)。 查询返回的对象处于持久(persistent)状态。如果知道的查询只会返回一个对象,可使用list()的快捷方式uniqueResult()。 注意,使用集合预先抓取的查询往往会返回多次根对象(他们的集合类都被初始化了)。可以通过一个集合来过滤这些重复对象。 load()方法可以返回代理物件,并可充分利用缓冲机制。
以将HQL撰写在程式之外,以避免硬编码(Hard code)外置命名查询(Externalizing named queries)
可以在映射文件中定义命名查询(named queries)。 (如果的查询串中包含可能被解释为XML标记(markup)的字符,别忘了用CDATA包裹起来。)
<query name="ByNameAndMaximumWeight"><![CDATA[
from eg.DomesticCat as cat
where cat.name = ?
and cat.weight > ?
] ]></query>
参数绑定及执行以编程方式(programatically)完成:
Query q = sess.getNamedQuery("ByNameAndMaximumWeight");
q.setString(0, name);
q.setInt(1, minWeight);
List cats = q.list();
请注意实际的程序代码与所用的查询语言无关,也可在元数据中定义原生SQL(native SQL)查询, 或将原有的其他的查询语句放在配置文件中,这样就可以让Hibernate统一管理,达到迁移的目的。
也请注意在<hibernate-mapping>元素中声明的查询必须有一个全局唯一的名字,而在<class>元素中声明的查询自动具有全局名,是通过类的全名加以限定的。比如eg.Cat.ByNameAndMaximumWeight。
Configuration也允许指定配置属性:
Configuration cfg = new Configuration()
.addClass(org.hibernate.auction.Item.class)
.addClass(org.hibernate.auction.Bid.class)
.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
.setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
.setProperty("hibernate.order_updates", "true");
SessionFactory可以创建并打开新的Session。一个Session代表一个单线程的单元操作,SessionFactory则是个线程安全的全局对象,只需要被实例化一次.创建一个HibernateUtil辅助类(helper class)来负责启动Hibernate和更方便地操作SessionFactory。
创建一个HibernateUtil辅助类(helper class)来负责启动Hibernate和更方便地操作SessionFactory。hibernate.cfg.xml把这个文件拷贝到源代码目录下面,这样它就位于classpath的根目录的最后。Hibernate在启动时会自动在classpath的根目录查找名为hibernate.cfg.xml的配置文件。
import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
Session在第一次被使用的时候,即第一次调用getCurrentSession()的时候,其生命周期就开始。然后它被Hibernate绑定到当前线程。当事务结束的时候,不管是提交还是回滚,Hibernate会自动把Session从当前线程剥离,并且关闭它。Hibernate Session的生命周期可以很灵活,但是绝不要把的应用程序设计成为每一次数据库操作都用一个新的Hibernate Session。
持久化类(Persistent Classes)所有的持久化类都必须有一个 默认的构造方法(可以不是public的),建议对持久化类声明命名一致的标识属性,建议使用一个可以为空(也就是说,不是原始类型)的类型 如:Integer。 Hibernate可以持久化有 default、protected或private的get/set方法的属性。
如果想把持久类的实例放入Set中(当表示多值关联时,推荐这么做)或希望Set有明确的语义,就必须实现equals() 和hashCode()。
建议使用业务键值相等(Business key equality)来实现equals() 和 hashCode()。业务键值相等的意思是,equals()方法 仅仅比较形成业务键的属性,它能在现实世界里标识的实例(是一个自然的候选码)。
关联映射
使用Java的集合类(collection):Set,因为set 不包含重复的元素及与无关的排序。
1.多对多关联(或叫n:m实体关系), 需要一个关联表(association table)。表里面的每一行代表从person到event的一个关联。表名是由set元素的table属性配置的。关联里面的标识符字段名,对于person的一端,是由<key>元素定义,而event一端的字段名是由<many-to-many>元素的column属性定义。
使用Hibernate的多对多映射:
<class name="events.Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>
<property name="age"/>
<property name="firstname"/>
<property name="lastname"/>
<set name="events" table="PERSON_EVENT">
<key column="PERSON_ID"/>
<many-to-many column="EVENT_ID" class="events.Event"/>
</set>
</class>
关联工作把一些people和events 一起放到EventManager的新方法中:
private void addPersonToEvent(Long personId, Long eventId) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Person aPerson = (Person) session.load(Person.class, personId);
Event anEvent = (Event) session.load(Event.class, eventId);
aPerson.getEvents().add(anEvent);
session.getTransaction().commit();
}
set元素的inverse="true"属性。
这意味着在需要的时候,Hibernate能在关联的另一端的类得到两个实体间关联的信息。
使双向连起来首先请记住,Hibernate并不影响通常的Java语义。 在单向关联的例子中,是怎样在Person和Event之间创建联系的?把Event实例添加到Person实例内的event引用集合里。因此很显然,如果要让这个关联可以双向地工作,需要在另外一端做同样的事情 - 把Person实例加入Event类内的Person引用集合。这“在关联的两端设置联系”是完全必要的而且都得这么做。
许多开发人员防御式地编程,创建管理关联的方法来保证正确的设置了关联的两端,比如在Person里:
protected Set getEvents() {
return events;
}
protected void setEvents(Set events) {
this.events = events;
}
public void addToEvent(Event event) {
this.getEvents().add(event);
event.getParticipants().add(this);
}
public void removeFromEvent(Event event) {
this.getEvents().remove(event);
event.getParticipants().remove(this);
}
注意现在对于集合的get和set方法的访问级别是protected - 这允许在位于同一个包(package)中的类以及继承自这个类的子类可以访问这些方法,但禁止其他任何人的直接访问,避免了集合内容的混乱。应尽可能地在另一端也把集合的访问级别设成protected。
inverse映射属性究竟表示什么呢?对于和Java来说,一个双向关联仅仅是在两端简单地正确设置引用。然而,Hibernate并没有足够的信息去正确地执行INSERT和UPDATE语句(以避免违反数据库约束),所以它需要一些帮助来正确的处理双向关联。把关联的一端设置为inverse将告诉Hibernate忽略关联的这一端,把这端看成是另外一端的一个镜象(mirror)。这就是所需的全部信息,Hibernate利用这些信息来处理把一个有向导航模型转移到数据库schema时的所有问题。只需要记住这个直观的规则:所有的双向关联需要有一端被设置为inverse。在一对多关联中它必须是代表多(many)的那端。而在多对多(many-to-many)关联中,可以任意选取一端,因为两端之间并没有差别。
不要为每次数据库操作都使用一个新的Hibernate Session。将Hibernate Session的范围设置为整个请求。要用getCurrentSession(),这样它自动会绑定到当前Java线程。
Query上有list()与iterate()方法,两者的差别在于开启Query之后,list()方法在读取资料时,会利用到Query快取,
而iterate()则不会使用到Query快取功能,而是直接从资料库中再查询资料,执行两次数据库的查询。
批量处理:
Hibernate 把所有新插入实例在session级别的缓存区进行了缓存,如果要执行批量处理并且想要达到一个理想的性能, 那么使用JDBC的批量(batching)功能是至关重要。将JDBC的批量抓取数量(batch size)参数设置到一个合适值 (比如,10-50之间):
hibernate.jdbc.batch_size 20
注意,假若使用了identiy标识符生成器,Hibernate在JDBC级别透明的关闭插入语句的批量执行。
使用延迟加载时,由于在需要数据时会向数据库进行查询,所以session不能关闭,如果关闭会丢出LazyInitializationException异常如果使用了延迟加载,而在某些时候仍有需要在session关闭之后取得相关对象,则可以使用Hibernate.initialize()来先行载入相关对象,
批量插入(Batch inserts)
如果要将很多对象持久化,必须通过经常的调用 flush() 以及稍后调用 clear() 来控制第一级缓存的大小。
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
if ( i % 20 == 0 ) { //20, same as the JDBC batch size //20,与JDBC批量设置相同
//flush a batch of inserts and release memory:
//将本批插入的对象立即写入数据库并释放内存
session.flush();
session.clear();
}
}
tx.commit();
session.close();
批量更新(Batch updates)
此方法同样适用于检索和更新数据。此外,在进行会返回很多行数据的查询时, 需要使用 scroll() 方法以便充分利用服务器端游标所带来的好处。
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
ScrollableResults customers = session.getNamedQuery("GetCustomers")
.setCacheMode(CacheMode.IGNORE)
.scroll(ScrollMode.FORWARD_ONLY);
int count=0;
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
if ( ++count % 20 == 0 ) {
//flush a batch of updates and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
可以在映射文档中定义查询的名字,然后就可以象调用一个命名的HQL查询一样直接调用命名SQL查询.在这种情况下,不 需要调用addEntity()方法.
<sql-query name="persons">
<return alias="person" class="eg.Person"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
</sql-query>
List people = sess.getNamedQuery("persons")
.setString("namePattern", namePattern)
.setMaxResults(50)
.list();
<idbag>映射定义了代理键,因此它总是可以很高效的被更新。事实上, <idbag>拥有着最好的性能表现。
双向的一对多关系:
要实现一个简单的从Parent到Child的<one-to-many>关联
<many-to-one name="parent" column="parent_id" not-null="true"/>
(还需要为类Child添加parent属性)
现在实体Child在管理连接的状态,为了使collection不更新连接,使用inverse属性。
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
下面的代码是用来添加一个新的Child
Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
c.setParent(p);
p.getChildren().add(c);
session.save(c);
session.flush();
现在,只会有一条INSERT语句被执行!
为了让事情变得井井有条,可以为Parent加一个addChild()方法。
public void addChild(Child c) {
c.setParent(this);
children.add(c);
}
现在,添加Child的代码就是这样
Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.save(c);
session.flush();
需要显式调用save()仍然很麻烦,可以用级联来解决这个问题。
<set name="children" inverse="true" cascade="all">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
这样上面的代码可以简化为:
Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.flush();
同样的,保存或删除Parent对象的时候并不需要遍历其子对象。 下面的代码会删除对象p及其所有子对象对应的数据库记录。
Parent p = (Parent) session.load(Parent.class, pid);
session.delete(p);
session.flush();
加载并存储对象
session.save(theEvent);
List result = session.createQuery("from Event").list();
spring与hibernate的整合原理:HibernateTemplate封装了session,StudentDAO需要一个HibernateTemplate,即可通过HibernateTemplate完成Session的功能。怎样创建出HibernateTemplate呢?HiberateTemplate需要SessionFactory,LocalSessionFactoryBean创建出hibernate的SessionFactory,即StudentDAO-->HibernateTemplate-->LocalSessionFactoryBean。如果StudentDAO直接得到了SessionFactory,那么,它也可以通过内部的程序代码创建出HibernateTemplate,HibernateDAOSupport就是基于这种考虑设计出来,它内封装了HibernateTemplate,并且需要给它传递SessionFactory。LocalSessionFactoryBean是如何创建出hibernate的SessionFactory的,hibernate.cfg.xml文件中的配置项都可以通过程序来设置,所以,在spring中可以不用hibernate.cfg.xml文件。
get与load都可以根据参数获取到指定的实体,有什么区别呢??
从Hibernate的参考手册中,基本可以总结出这样几条:
1、如果找不到符合条件的记录,get方法返回null,而load方法抛出异常
2、使用load方法,一般都假定要取得对象肯定是存在的,而get方法则尝试,如果不存在,就返回null
从这个角度看,似乎没什么大不了的。其实,仔细看看hibernate中关于get和load方法的源码,就不难发现,这背后的不同了。
get方法每次都要访问数据库,而load则不一定,如果使用了缓存机制,load就会从缓存中查找,所以,不一定每次都访问数据库。也就是,load可以更好的利用hibernate的缓存机制,从有效地降低地数据库的直接操作。经过比较可以发现:Session.load/get方法均可以根据指定的实体类和id从数据库读取记录,并返回与之对应的实体对象。其区别在于:
如果未能发现符合条件的记录,get方法返回null,而load方法会抛出一个ObjectNotFoundException。
Load方法可返回实体的代理类实例,而get方法永远直接返回实体类。
load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。
异常1:not-null property references a null or transient value
解决方法:将“一对多”关系中的“一”方,not-null设置为false
(参考资料:http://www.thearcmind.com/confluence/pages/viewpage.action?pageId=212)
异常2:org.hibernate.TransientObjectException: object references an unsaved transient instance
解决方法:cascade="save-update,persist"
(参考资料:http://www.laliluna.de/254.html)
异常3:org.hibernate.QueryException: could not resolve property
解决方法:"from Category category where category.userID = :userID"修改为"from Category category where userID = :userID"或者"from Category category where category.user.id = :userID"
(参考资料:http://www.laliluna.de/277.html)
异常4:could not initialize proxy - the owning Session was closed
解决方法:设置lazy为false
(参考资料:http://forum.springframework.org/showthread.php?t=27993)
复合主键:
主键即可以和其他字段定义在一个类中,也可以将复合主键单独声明为一个类,然后在类中声明一个复合主键类的对象。
在此介绍第一种方式:
CREATE TABLE user (
name VARCHAR(100) NOT NULL,
password VARCHAR(50) NOT NULL,
age INT,
PRIMARY KEY(name, password)
);
name和password为复合主键,Hibernate要求复合主键类别要实现Serializable,并定义equals()和hashCode()方法。
import java.io.Serializable;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
public class User implements Serializable {
private String name;
private String password;
private Integer age;
public User() {}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
}
// 必须重新定义equals()与hashCode()
public boolean equals(Object obj) {
if(obj == this) {
return true;
}
if(!(obj instanceof User)) {
return false;
}
User user = (User) obj;
return new EqualsBuilder().append(this.name, user.getName()).append(this.password, user.getPassword()).isEquals();
}
public int hashCode() {
return new HashCodeBuilder().append(this.name).append(this.password).toHashCode();
}
}
<composite-id>在映射文件中定义复合组件与对象的属性对应。
<composite-id>
<key-property name="name" column="name" type="java.lang.String"/>
<key-property name="password" column="password" type="java.lang.String"/>
</composite-id>
Hibernate 3中引入了动态模式,可以使用容器充当Java实体,在构造系统原型时灵活变化,而不必实际定义Java对象。
直接在映射文件的<class>标签上使用entity-name属性:
<class entity-name="EntityName" table="T_NAME">
entity-name属性设定的名称将在储存或载入时使用,例如可以如下储存:
Map entity = new HashMap();
entity.put("name", "zxz");
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx= session.beginTransaction();
session.save("EntityName", entity);
tx.commit();
session.close();
Map容器的key用来表示属性名称,而value用来表示值。
加载:
Map entity = (Map) session.load("EntityName", new Long(1));
System.out.println(entity.get("name"));
使用HQL查询:EntityName为配置文件中定义的entity_name属性值。
List entitys = session.createQuery("from EntityName").list();
for(int i = 0; i < entitys.size(); i++) {
Map entity = (Map) entitys.get(i);
System.out.println(entity.get("name"));
}
为了设计上的弹性,动态模型也可以与静态的POJO模型混用,要混用静态模型与动态模型,可在配置文件中设定name与entity-name属性:
<class name="ClassName" entity-name="EntityName" table="T_NAME">
默认是使用pojo来操作,要使用动态模型必须:
Map entity = new HashMap();
entity.put("name", "zxz");
Session dynamicSession = session.getSession(EntityMode.MAP);
Transaction tx= dynamicSession.beginTransaction();
dynamicSession.save("EntityName", entity);
继承关系:Hibernate自动判断继承关系。
可以采取三种策略,
第一种:
(1)Table per concrete class,即抽象父类不建表,每一个子类建立一个表格。抽出相同属性部分定义为抽象父类,然后分别定义继承父类的子类,并分别定义配置文件。(HQL:from ParentClass 多表多次查询)。
分析:这种方式建议用于没有关联性,而且父类将来不会修改的情况,或者不需要多表查询的情况。
(2)也可在一个配置文件中,将父类与子类的属性对应撰写在一起。只建立一个配置文件(多表一次查询)。
<!-- abstract 表明ParentClass是抽象的,无需对应至任何表格 -->
<class name="ParentClass" abstract="true">
<id name="id">
<generator class="increment"/>
</id>
<!-- 从父类别继承下来的共同属性 -->
<property name="ParentProperty"/>
<!-- 子类别的新增属性 -->
<union-subclass name="subClass1" table="T_NAME1">
<property name="someProperty"/>
</union-subclass>
<!-- 子类别的新增属性 -->
<union-subclass name="subClass2" table="T_NAME2">
<property name="otherProperty"/>
</union-subclass>
</class>
分析:新增时要先查询出最大ID值再分别进行插入。利用子查询在同一个SQL语句中完成所有查询。Table per concrete class的继承映射方式是最简单,但没有效率。
第二种:Table per subclass。父类与子类分别建表,而父类与子类对应的表通过外键来产生关联。只建立一个配置文件。
(多表一次查询,外键连接查询)
<class name="ParentClass" table="T_NAME">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="ParentClassProperty"/>
<joined-subclass name="SubClass1" table="T_SUBNAME1">
<key column="id" foreign-key="id"/>
<property name="someProperty" column="someProperty" />
</joined-subclass>
<joined-subclass name="SubClass2" table="T_SUBNAME2">
<key column="id" foreign-key="id"/>
<property name="otherProperty" column="otherProperty" />
</joined-subclass>
</class>
###<joined-subclass>指明了子类所对应的表,<key column>指明子类对应表中,与父类的主键对应的外键一致。
分析:效率是这个映射类型需要考虑的,在复杂的类别继承下,新增资料必须对多个表格进行,而查询时,跨越多个表格的join也可能引发效率上的问题。如果需要多表查询,而子类相对来说有较多新增的属性,则可以使用这种映射方式。
第三种:
(1)Table per class hierarchy的继承映射方式,这种方式使用一个表储存同一个继承级别的所有类,并使用额外的属性来表示所记录的是哪一个子类的资料。
create table T_NAME (
id bigint not null auto_increment,
subType varchar(255) not null,
name varchar(255),
someProperty varchar(255),
otherProperty varchar(255),
primary key (id)
)
如果要储存的资料是来自SubClass1,则在subType记下"sub1",如果储存的资料来subClass2,则在subType记下"sub2",由subType就可以判断是要使用subClass1或subClass2,在映射文件中使用<discriminator>等相关标签来定义。
<class name="User" table="T_USER">
<id name="id" column="id">
<generator class="native"/>
</id>
<discriminator column="subType"/>
<property name="publicProperty"/>
<subclass name="SubClass1" discriminator-value="sub1">
<property name="someProperty" column="someProperty" />
</subclass>
<subclass name="SubClass2" discriminator-value="sub2">
<property name="otherProperty" column="otherProperty"/>
</subclass>
</class>
分析:因子类属性的不同,所以储存时会有许多字段没有值,但查询效率较好。
(2)也可以不使用专门定义一个字段来记录子类的类型,这适用于在使用一个已有数据库的情况,无法新增字段来记录子类类型。
配置文件只需要修改 <discriminator column="subType"/>为<discriminator formula="case when someProperty is not null then 'sub1' else 'sub2' end"/> 即可。在<discriminator>上,设定foumula属性,根据回传值为sub1或sub2来判断是哪个子类。
分析:在需要多表查询,而子类属性相对比较少时,可以使用这种映射方式。这种方式会有大量的字段为NULL的情况,好处是使用一个表,查询时只需一次SQL。
Set:
(1)非实体(Entiy)时的映射方式,简单的说,也就是所包括的对象没有主键(Identity),只是纯綷的值类型(Value type)。
例:为了不允许重复的邮件位址记录,所以使用Set物件。在所包含的类中定义:
private Set emails;
public Set getEmails() {
return emails;
}
public void setEmails(Set emails) {
this.emails = emails;
}
public void addEmail(String email) {
this.emails.add(email);
}
public void removeEmail(String email) {
this.emails.remove(email);
}
要映射Set集合,可以使用另一个表来储存Set集合中的资料.
create table email (
id bigint not null,
address varchar(255)
)
create table user (
id bigint not null auto_increment,
name varchar(255),
primary key (id)
)
alter table email add index id (id),
add constraint id foreign key (id) references user (id)
<class name="User" table="user">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<set name="emails" table="email">
<key column="id" foreign-key="id"/>
<element type="string" column="address"/>
</set>
</class>
User user1 = new User();
user1.setEmails(new HashSet());
user1.setName("Name");
user1.addEmail("name@gmail.com");
user1.addEmail("name@yahoo.com");
User user2 = new User();
user2.setEmails(new HashSet());
user2.setName("name1");
user2.addEmail("name1@gmail.com");
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
session.save(user1);
session.save(user2);
tx.commit();
session.close();
List是有序的结构:
private List items;
public List getItems() {
return items;
}
public void setItems(List items) {
this.items = items;
}
public void addItem(String item) {
items.add(item);
}
public void removeItem(String item) {
items.remove(item);
}
create table item (
id bigint not null,
name varchar(255),
position integer not null,
primary key (id, position)
)
create table user (
id bigint not null auto_increment,
name varchar(255),
primary key (id)
)
alter table item
add index id (id),
add constraint id
foreign key (id)
references user (id)
<class name="User" table="user">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<list name="items" table="item">
<key column="id"/>
<list-index column="position"/>
<element column="name" type="string"/>
</list>
</class>
User user1 = new User();
user1.setItems(new ArrayList());
user1.setName("caterpillar");
user1.addItem("DC");
user1.addItem("CF Card");
User user2 = new User();
user2.setItems(new ArrayList());
user2.setName("momor");
user2.addItem("comics");
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
session.save(user1);
session.save(user2);
tx.commit();
session.close();
Map的特性是key/value对,容器中的每一个对象都有一个key与之对应,所以将Map集合的资料储存至数据库时,必须一同储存它的key值。
private Map items;
public Map getItems() {
return items;
}
public void setItems(Map items) {
this.items = items;
}
public void addItem(String name, String description) {
items.put(name, description);
}
public void removeItem(String name) {
items.remove(name);
}
create table item (
id bigint not null,
description varchar(255),
name varchar(255) not null,
primary key (id, name)
)
create table user (
id bigint not null auto_increment,
name varchar(255),
primary key (id)
)
alter table item
add index id (id),
add constraint id
foreign key (id)
references user (id)
<class name="onlyfun.caterpillar.User" table="user">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<map name="items" table="item">
<key column="id" foreign-key="id"/>
<map-key column="name" type="string"/>
<element column="description" type="string"/>
</map>
</class>
User user1 = new User();
user1.setItems(new HashMap());
user1.setName("caterpillar");
user1.addItem("Book", "Java Gossip");
user1.addItem("DC", "Caxxx A80");
User user2 = new User();
user2.setItems(new HashMap());
user2.setName("momor");
user2.addItem("Doll", "Snoppy world");
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
session.save(user2);
tx.commit();
session.close();
如果希望Set、Map等集合可以依一定的顺序来排列,可以从两个方面排序,一是在载入资料后于JVM中排序,另一是在数据库中直接使用order by子句来排序。要在JVM中进行排序,可以在映射文件中使用sort属性来定义集合的排序,这适用于Set与Map。Bag与List并不适用于这种方式,Bag或List本身是根据索引值来排列的。
<set name="emails" table="email" sort="natural">
sort="natural"表示使用对象的comparaTo()方法来进行排序,集合中的对象必须实现java.lang.Comparable接口。
另一种排序的方式则是在数据库中进行,直接使用order by子句来排序,这可以在映射文件中使用order-by属性来指定。
<set name="emails" table="email" order-by="address desc">
Hibernate在内部会使用LinkedHashMap或LinkedHashSet来作为集合,如果是Bag的话,则会在内部使用ArrayList作为集合。
双向关联(inverse 的意义)
在一对多、多对一形成双向关联的情况下,可以将关联维持的控制权交给多的一方,这样会比较有效率.所以在一对多、多对一形成双向关联的情况下,可以在“一”的一方设定控制权反转,也就是当储存“一”的一方时,将关联维持的控制权交给“多”的一方.<set name="users" table="user" cascade="save-update" inverse="true">
session.flush();强制存储对象.
session.evict(Object);将对象从Cache中删除.
session.clear();删除所有Cache中对象.
在SQL Server、Oracle等数据库中,可以在Hibernate配置文件中设定属性hibernate.jdbc.batch_size来控制每多少条记录就存储至数据库.
<session-factory>
<property name="hibernate.jdbc.batch_size">100</property>
</session-factory>
在MySQL中暂不支持该功能。
Hibernate本身并未提供二级缓存的实现,而是由第三方(Third-party)产品来实现,Hibernate预设使用EHCache作为其二级缓存的实现,在最简单的情况下,只需在Hibernate下撰写一个ehcache.xml作为EHCache的资源定义文件,可以在 Hibernate下载档案中的etc目录下找到一个已经撰写好的ehcache.xml
sessionFactory.evict(User.class, user.getId());消除二级缓存.
如果打算在Hibernate中使用其它第三方产品进行缓存,则可以在hibernate.cfg.xml中定义 hibernate.cache.provider_class属性<property name="hibernate.cache.provider_class"> org.hibernate.cache.HashtableCacheProvider </property>
HashtableCache是Hibernate自身提供的二级缓存实现,不过性能与功能上有限,只用于开发时期的测试之用。
发表评论
-
JEECG我来了!
2013-05-04 17:12 339最 ... -
windows xp CVSNT
2009-02-06 14:22 2191环境:win xp sp2+cvsnt:2.5.04.3236 ... -
properties几点注意事项
2008-12-11 09:09 2988在项目中使用配置文件*.properties的几点注意事项:p ... -
Log4j多个日志文件
2008-11-24 16:51 4833Log4j的基本使用不再在此细说,在工作中有需求要将错误日志信 ... -
JMAGE 处理图片、给图片加水印
2008-08-01 13:56 2068战友是梦,睡也思恋;战友是魂,嵌入心田;战友是金,沉沉甸甸;战 ... -
Struts <html:multibox> <html:select>
2008-07-01 08:51 3267庆贺党的生日! 一、 ... -
利用开源组件制作验证码
2008-06-20 09:47 3839jsp验证码制作 介绍两个功能强大的开源制作验证码组件 ... -
ExtremeTable
2008-05-29 08:56 1622目前开源的JSP分页组件已有很多,像displayTag, ... -
Struts1.X
2008-04-29 09:10 1362文章主要内容是介绍本人在使用Struts过程中常用到的一些特性 ... -
JAVA IO 学习
2008-03-31 08:54 1141简单的创建一个文件: ... -
JAVA中获取路径
2008-02-29 12:59 4274JAVA中获取路径: 1.jsp中取得路径: 以工 ... -
JAVA处理日期时间
2007-12-29 09:43 4948JAVA处理日期时间常用方法: 1.java.util.Ca ... -
JSTL 展现页面内容
2007-09-10 10:16 2475祝恩师们节日快乐! JSP 标准标记库(JS ... -
Struts+Spring+Hibernate处理Lob(Blob,Clob)
2007-08-13 15:18 8316Struts+Spring+Hibernate处理Lob(B ... -
简述Abstract Class和Interface
2007-08-02 08:57 1505Abstract Class和Interfa ... -
轻松上传文件
2007-08-01 10:04 1831献给建军八十周年 文件上传在JAVA应用中比 ... -
JasperReport+iReport
2007-07-05 17:00 7066JasperForge.org 是开源门户网站 http ... -
Birt初探
2007-06-25 13:38 4716报表是作为企业应用中比较常用的功能模块 ...
相关推荐
Hibernate 配置各种数据库 Hibernate 是一个基于 Java 的持久层框架,提供了一个抽象的数据访问层,能够与多种数据库进行集成。在 Hibernate 的配置文件中,我们可以配置不同的数据库连接,包括驱动程序、URL 等...
"Hibernate入门到精通" Hibernate 是一个基于Java的ORM(Object-Relational Mapping,对象关系映射)框架,它提供了一种简洁高效的方式来访问和操作关系数据库。下面是 Hibernate 的主要知识点: Hibernate 简介 ...
Hibernate.jar包,Hibernate可以应用在任何使用JDBC的场合,包含 hibernate-commons-annotations-4.0.1.Final.jar hibernate-core-4.1.12.Final.jar hibernate-ehcache-4.1.12.Final.jar hibernate-entitymanager-...
Hibernate3 是一个非常重要的Java持久化框架,它简化了数据库操作,使得开发人员可以更加专注于业务逻辑而不是数据库的细节。这个`hibernate3.zip`压缩包包含了`hibernate3.jar`,它是Hibernate 3版本的核心库,包含...
Hibernate是一个开源的对象关系映射(ORM)框架,它允许Java开发者使用面向对象的方式来操作数据库,极大地简化了数据访问层的编程工作。这个压缩包包含了Hibernate的基础jar包,这些jar文件是开发Hibernate应用所...
在Java开发环境中,与KingbaseV8数据库进行交互通常会用到Hibernate框架和JDBC驱动。 Hibernate是一个优秀的对象关系映射(ORM)框架,它简化了Java应用程序对数据库的操作,通过将Java对象与数据库表进行映射,...
标题中的“hibernate和MySQL的jar”指的是Hibernate ORM框架与MySQL数据库之间的连接库。Hibernate是一种流行的Java对象关系映射(ORM)工具,它允许开发者使用面向对象的编程方式来操作数据库,而无需直接编写SQL...
Hibernate 是一个开源的对象关系映射(ORM)框架,它允许Java开发者使用面向对象的方式来操作数据库。这个框架将数据库操作转化为对Java对象的操作,简化了数据持久化的复杂度。以下是对Hibernate的一些关键知识点的...
Hibernate3 是一个强大的Java持久化框架,它允许开发者将数据库操作与业务逻辑解耦,使得应用程序的开发更为简便。这个“hibernate3全部jar包:hibernate3.jar.zip”包含了所有必要的库文件,方便用户一次性下载并...
HibernateTools是Java开发人员在使用Hibernate ORM框架时的有力辅助工具集,主要目的是为了提高开发效率,简化数据库操作。在HibernateTools 3.2.4版本中,它包含了一系列的特性与插件,以支持更便捷地进行对象关系...
hibernate 5.2.15 hibernate 5.2.15 hibernate 5.2.15 hibernate 5.2.15 hibernate 5.2.15hibernate 5.2.15
Hibernate3 是一个非常重要的Java持久化框架,它允许开发者将对象模型与数据库关系模型进行映射,从而简化了数据存取的过程。这个压缩包“hibernate3必要jar包”显然是针对Hibernate3版本的,已经去除了冗余的库文件...
【描述】中的"hibernate的jar包"指的是Hibernate框架的运行库文件,这些JAR文件包含了Hibernate的所有核心API、实现和依赖库,如Hibernate Commons Annotations、Hibernate EntityManager、Hibernate Core等。...
Hibernate3是一个广泛使用的Java对象关系映射(ORM)框架,它允许开发者用面向对象的方式处理数据库操作,极大地简化了Java应用程序与数据库之间的交互。在这个"Hibernate3的依赖包"中,包含了运行Hibernate3应用...
标题"Hibernate 中文api 等学习资料"暗示了这是一组针对Hibernate ORM框架的中文学习资源,包括API文档和其他指南,旨在帮助用户更好地理解和使用Hibernate。 描述中的"hibernate orm框架api中文文档,学习资料,...
《hibernate-extensions与Middlegen-Hibernate:数据库到Java对象的自动化转换》 在Java的持久化层开发中,Hibernate作为一款强大的ORM(对象关系映射)框架,极大地简化了数据库操作。然而,手动编写实体类和映射...
《深入理解Hibernate配置与映射:hibernate-configuration-3.0.dtd与hibernate-mapping-3.0.dtd解析》 在Java世界里,Hibernate作为一款强大的对象关系映射(ORM)框架,极大地简化了数据库操作。而`hibernate-...
这里我们关注的是“达梦Hibernate方言2.0至4.0”,这是一个专为达梦数据库系统(DM)设计的Hibernate方言支持,涵盖了Hibernate框架的多个版本。Hibernate是一个流行的Java对象关系映射(ORM)框架,它允许开发人员...
hibernate,hibernate,hibernate,hibernate,hibernate,hibernate,hibernate,hibernate,hibernate,hibernate,hibernate,hibernate,hibernate,hibernate,hibernate,hibernate,包含4个说明文档,分别详细解说了hibernate...
根据提供的标题“深入浅出 Hibernate.pdf”以及描述“深入浅出 Hibernate.pdf”,我们可以推断这份文档主要讲解了Hibernate框架的相关知识。接下来,我们将基于这些信息展开详细的解释与知识点的梳理。 ### ...