1.结构
Session:负责保存、更新、删除、加载和find()。
Transaction:管理事务。 session.beginTransaction(); tx.commit;
Query和Criteria接口:执行数据库查询。
Query query = session.createQuery("from OaAdrgrpB where groupname = :name");
query.setString("name", name_f);
Configuration接口:配置Hibernate,根启动Hibernate,创建SessionFactory对象。
SessionFactory接口:初始化Hibernate,创建Session对象。
2. spring封装的类:
(1) HibernateDaoSupport MyHibernateDao继承此类。
1) 需要注入 <propertyname="sessionFactory"><ref local="sessionFactory"/>
2) 继承了HibernateDaoSupport类的类获取session时,已不可用SessionFactory.OpenSessioon的形式来获取Session了,由于HibernateDaoSupport本身已有获取session的方法getSession(),所以直接用Session se=this.getSession();来获取,
2) 在继承 HibrnateDaoSupport的 DAO 实现里,Hibernate Session 的管理完全不需要Hibernate代码打开,而由 Spring 来管理。 Spring 会根据实际的操作,采用"每次事务打开一次 session" 的策略,自动提高了数据库访问的性能。
3) 在依据hql获取用户信息时,继承了HibernateDaoSupport类的类中不能在使用Query类了,而是用List<Ssh> list = this.getHibernateTemplate().find(hql);形式来获取实体类集合
(2)HibernateTemplate 提供了非常多的常用方法来完成基本的操作,比如增加、删除、修改及查询等操作,Spring 2.0 更增加对命名 SQL 查询的支持,也增加对分页的支持。
hibernateTemplate = new HibernateTemplate(sessionFactory);
(3)Spring+hibernate 访问数据库有三种方法 :
1) 继承 继承 HibernateDaoSupport 。 注入 SessionFactory
在 spring 配置文件中,对 Dao 注入 sessionFactory. 比较简单。
如 :
< bean id = "UserInfoDao" class = "com.hr2job.dao.impl.UserInfoDaoImpl">
< property name = "sessionFactory" ref = "sessionFactory"></ property >
</ bean >
优点:不再需要关心关闭、是否连接成功等问题。主要是很方便。缺点:继承了,不能继承其它类了。
2) 在自己的DAO 注入 HibernateTemplate
要先配置好 HibernateTemplate:
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
3) 注入 jdbcTemplate
2. Hibernate连接池的配置
方式1:使用Hibernate自带的连接池。 (性能差,有小bug)
方式2:使用配置文件指定的数据库连接池。 (c3po,proxool)
方式3:从容器中获取得到连接池(如:Tomcat)
在hibernate.cfg.xml中
方式1:
<hibernate-configuration>
<session-factory >
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/feifei </property>
方式3:
假定已经配好了一个JNDI名为“jdbc/feifei”的数据源。 在hibernate.cfg.xml中指定使用这个名为“jdbc/feifei”的JNDI
<property name=”hibernate.connection.datasource”>java:/comp/env/jdbc/feifei </property>
3. cascade和inverse
有省份表和城市表,其中城市表有一个外键province_id引用到省份表的主键。这样,可以把省份表看成是父表,把城市表看成是子表,城市表记录的存在依赖于省份表的记录,城市表有一个外键province_id引用到省份表的主键,在Hibernate变成了双向的映射关系:City类有一个类型为Province的province属性,关联到省份表,在映射文件中是many-to-one的关系;Province类有一个Set<City>的cities属性,关联到城市表,在映射文件中是one-to-many的关系。
在Hibernate的映射文件里,同样可以设置cascade属性来控制父子关系。通常在父表设置cascade属性,有以下几种情况:
1、没有设置cascade属性
用方法addInNoCascade()增加记录“广东”和“广州”,再用方法delete()删除“广东”,将会出现异常,系统会说因为“广东”被城市表外键关联了而不能删除。
cascade属性通常在one-to-many关系里应用
第2个外键是用SQL建表时生成的,设置了on delete cascade;而第1个外键应该是用Hibernate操作数据库时,Hibernate自动建立的。第1个外键的On delete被设置为No action,因此删除“广东”的时候,受到这个外键的限制,导致删除失败。
2、设置cascade属性为delete-orphan
在映射文件Province.hbm.xml中,在one-to-many关系对应的Set里,设置cascade="delete-orphan",此功能与MySQL里设置外键设置为on delete cascade相同。再用方法delete()删除“广东”,删除成功。即是,设置cascade为delete-orphan以后,对删除父表记录的时候,会同时删除子表的相关记录。
3、设置cascade属性为all
cascade的属性,除了可以是delete-orphan,还可以是create、update、delete、all等等。all代表除 delete-orphan以外的所有属性值,当设置cascade为all以后,对父表记录的增加、修改操作,会影响到子表的相关记录。
在映射文件Province.hbm.xml中,在one-to-many关系对应的Set里,设置cascade="all"。用方法 addInCascadeOfAll()增加记录“广东”,方法里只有save“广东”,并没有save“深圳”,只是用属性关联了“广东”和“深圳”的关系。结果显示,深圳也被添加到数据库里,这就是cascade="all"的作用,使对父表的操作影响到子表。
注意:A、delete-orphan是一个特别的属性值,只能应用在one-to-many关系的cascade属性。
B、cascade属性通常在one-to-one和one-to-many关系里应用,不推荐在many-to-one或者many-to- many关系里应用。
inverse属性决定是否把对set的改动反映到数据库中去。
inverse=false————反映(默认);inverse=true————不反映”
* inverse只对set+one-to-many(或many-to-many)有效,对many-to-one, one-to-one无效。
例:在一对多关系中的province 1------> 0..* city, province.citys.add(new City());
inverse=false, cascade=all,则执行save(province)时SQL语句
insert into province (pid, pname) values (01, "广东“) --这一条都会有
insert into city (cid,cname,pid) values (001, "深圳“, 01) --由于设置了cascade=all,这意味着同时维护子表
update city set pid=01 where cid=001 --由于设置了inverse=false, 这意味着porvince还要维护关系
2.排序
(1) 内存排序
映像文件中设定sort属性,例如若为Set,则如下设定:
<set name="addrs" table="ADDRS" sort="natural" >
<key column="USER_ID"/>
<element type="string" column="ADDRESS" not-null="true"/>
</set>
藉由指定sort为natural,Hibernate在加载数据库的数据时,将使用java.util.SortedSet型态对象,如果是String,则根据compareTo()方法来进行排序。
如果是Map的话,则如下设定:
<map name="files" table="FILES" sort="natural">
<key column="USER_ID"/>
<index column="DESCRIPTION" type="string"/>
<element type="string" column="FILENAME" not-null="true"/>
</map>
上面的设定将使用java.util.SortedTree,根据DESCRIPTION进行排序,sort除了设定natural之外,也可以指定一个实现java.util.Comparator的类别名称。
eg:
public class UpPurviewComparator implements Comparator{
public UpPurviewComparator() { }
public int compare(Object o1, Object o2){
if(o1 == null){
return o2 == null ? 0 : 1;
}
if(o2 == null){
return -1;
}
if(o1 instanceof UpPurview && o2 instanceof UpPurview){
UpPurview p1= (UpPurview)o1;
UpPurview p2= (UpPurview)o2;
String name1=p1.getModelname();
String name2=p2.getModelname();
if(p1.getId().compareTo(p2.getId())==0) return 0;
if(name1.compareTo(name2)>0) return -1;
if(name1.compareTo(name2)<0) return 1;
else return -1;
}
return -1;
}
}
<set name="upPurviews" cascade="none" table="UP_ROLE_PV_R" sort="com.ztenc.oa.mfp.dao.user.UpPurviewComparator" >
<key>
<column name="role_id" not-null="true" />
</key>
<many-to-many column="purview_id" class="com.ztenc.oa.mfp.bean.UpPurview" />
</set>
private Set upPurviews = new TreeSet(new UpPurviewComparator());
2.数据库排序
<set name="addrs" table="ADDRS" order-by="ADDRESS desc" >
<key column="USER_ID"/>
<element type="string" column="ADDRESS" not-null="true"/>
</set>
在类中:
private Set properties= new TreeSet ();
在Map中也是相同的设定方式,您也可以利用数据库中的函式功能,例如:
<map name="files" table="FILES" order-by="lower(FILENAME)">
<key column="USER_ID"/>
<index column="DESCRIPTION" type="string"/>
<element type="string" column="FILENAME" not-null="true"/>
</map>
使用这个方法进行排序时,Hibernate会使用LinkedHashSet或LinkedHashMap实现查询时的排序,所以这个方法仅适用于JDK 1.4或以上的版本。
2.检索
(1) HQL检索
Query query=session.createQuery("from Customer as c where "
+" c.name=:customerName "
+"and c.age=:customerAge");
//动态绑定参数
query.setString("customerName","Tom");
query.setInteger("customerAge",21);
//执行查询语句,返回查询结果
List result= query.list();
(2) QBC
QBC检索方式
//创建一个Criteria对象
Criteria criteria=session.createCriteria(Customer.class);
//设定查询条件,然后把查询条件加入到Criteria中
Criterion criterion1= Expression.like("name", "T%") ;
Criterion criterion2= Expression.eq("age", new Integer(21)) ;
criteria=criteria.add(criterion1);
criteria=criteria.add(criterion2);
//执行查询语句,返回查询结果
List result=criteria.list();
(3) 分页查询
Query query = session.createQuery("from
Customer c
order by c.name asc");
query.setFirstResult(0);
query.setMaxResults(10);
List result = query.list();
(4)使用本地SQL查询
List cats = sess.createSQLQuery( " select * from cats " ).addEntity(Cat. class ).list();
这里,结果集字段名被假设为与映射文件中指明的字段名相同。对于连接了多个表的查询,这就可能造成问题,因为可能在多个表中出现同样名字的字段。下面的方法就可以避免字段名重复的问题:
List cats = sess.createSQLQuery( " select {cat.*} from cats cat " ).addEntity( " cat " , Cat. class ).list();
(5)命名SQL查询
可以在映射文档中定义查询的名字,然后就可以象调用一个命名的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();
3.数据关联
(1)一对多关系:
person 1--->* event
person.hbm.xml
<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" >
<key column="EVENT_ID"/>
<one-to-many class="events.Event"/>
</set>
</class>
运行代码:
Event theEvent=new Event();
theEvent.setTitle("lmb");
theEvent.setDate(new Date());
Person person = new Person();
person.setAge(20);
person.setFirstname("long");
person.setLastname("minbo");
person.getEvents().add(theEvent);
session.save(person);
session.getTransaction().commit();
运行异常:
Hibernate: insert into PERSON (age, firstname, lastname) values (?, ?, ?)
Hibernate: update EVENTS set EVENT_ID=? where EVENT_ID=?
Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: events.Event
分析原因:
配置文件设定结果:先保存person的内容,然后修改关系,并没有保存personr属性event,没有设定cascade
改正:
将 <set name="events" > 改为 <set name="events" cascade="all" >, 挺奇怪:加入和不加入table="EVENT"属性都是结果,系统会自动找到这个表格。cascade指当主控方执行操作时,关联对象(被动方)是否执行同一操作。如果设定为all,则代表无论主控方执行什么操作,都对其关联类执行同样的操作。
运行结果:
Hibernate: insert into PERSON (age, firstname, lastname) values (?, ?, ?)
Hibernate: insert into EVENTS (EVENT_DATE, title) values (?, ?)
Hibernate: update EVENTS set EVENT_ID=? where EVENT_ID=?
Exception in thread "main" org.hibernate.exception.GenericJDBCException: could not insert collection: [events.Person.events#3]
(2) 自身关联:
映射一对多双向自身关联关系(例如树型目录)
< many-to-one name = "parenCategory" column = "CATEGORY_ID" class = "Category" />
由于可以是 0 个或一个父类,所以没有设置 not-null = "true" 。
< set name = "childCategories" inverse = "true" cascade = "save-update" >
< key column = "CATEGORY_ID" />
< one-to-many class = "Category" />
</ set >
(3)多对多关联的inverse属性
inverse 属性默认是false的,就是说关系的两端都来维护关系。这个意思就是说,如有一个Student, Teacher和TeacherStudent表,Student和Teacher是多对多对多关系,这个关系由TeacherStudent这个表来表 现。那么什么时候插入或删除TeacherStudent表中的记录来维护关系呢?在用hibernate时,我们不会显示的对 TeacherStudent表做操作。对TeacherStudent的操作是hibernate帮我们做的。hibernate就是看hbm文件中指 定的是"谁"维护关系,那个在插入或删除"谁"时,就会处发对关系表的操作。前提是"谁"这个对象已经知道这个关系了,就是说关系另一头的对象已经set 或是add到"谁"这个对象里来了。前面说过inverse默认是false,就是关系的两端都维护关系,对其中任一个操作都会处发对表系表的操作。当在 关系的一头,如Student中的bag或set中用了inverse="true"时,那就代表关系是由另一关维护的(Teacher)。就是说当这插 入Student时,不会操作TeacherStudent表,即使Student已经知道了关系。只有当Teacher插入或删除时才会处发对关系表的 操作。所以,当关系的两头都用inverse="true"是不对的,就会导致任何操作都不处发对关系表的操作。当两端都是inverse= "false"或是default值是,在代码对关系显示的维护也是不对的,会导致在关系表中插入两次关系。