Hibernate hql时间比较
/**
* 获取现在时间
*
* @return 返回时间类型 yyyy-MM-dd HH:mm:ss
*/
public static java.sql.Timestamp getNowSqlDate() {
Date currentTime = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = formatter.format(currentTime);
java.sql.Timestamp currentTime_2 = java.sql.Timestamp.valueOf(dateString);
return currentTime_2;
}
返回24时制当前时间yyyy-MM-dd HH:mm:ss 如果yyyy-MM-dd hh:mm:ss则返回12时制的当前时间
hql代码
Map filterMap1=new HashMap();
filterMap1.put("kcjyid", kcjy.getId().toString());
filterMap1.put("nowtime", DateUtil.getNowSqlDate());
Collection<Object> teachksaplist=(Collection<Object>) serviceFactory.getAdminService().list1("from Ksap as b where b.kcjyId=:kcjyid and b.ksend <:nowtime order by b.ksend ", page, 1, filterMap1);
ksend 为Ksap 实体中的属性类型为Date
hibernate实现oracle的主键自增长
在很多情况下,我们使用Hibernate在已经建立好数据库的基础上。在oracle中,如果已经建立好的数据库中使用了sequence ,则可以按照下面的步骤把它引入到Hibernate中:
1、在oracle 首先创建sequence
create sequence seq_id
minvalue 1
start with 1
increment by 1
cache 20;
2.在你的hbm.xml中的配置
《id column=“ID0000” name=“id” type=“integer”》
《generator class=“sequence”》
《param name=“sequence”》seq_id《/param》
《/generator》
《/id》
这样再插入数据的时候,Hibernate回自动生成如下语句:
hibernate : select seq_id.nextval from dual
hibernate : insert into YXJK.T_YXJK_WHRYTXL (XM0000, ZW0000, LXDH00, SJHM00, DZYJ00,
IP0000, ID0000) values (?, ?, ?, ?, ?, ?, ?)
自动生成下一个序列值,然后将对象插入表中。
在使用的时候需要注意,Hibernate对于sequence的主键的要求是一定要是shor,long,或者integer
根据hibernate的文档,有两种方式实现实体对象的主键自动增长。
第一种:设置ID的增长策略是sequence,同时指定sequence的名字,最好每个表建一个sequence,此种做法就如同MS-SQL,MY-SQL中的自动增长一样,不需要创建触发器,具体的oracle数据库脚本及hibernate配置文件如下:
[1]oracle数据表的创建脚本:
Java代码
1. CREATE TABLE DEPARTMENT (
2. ID NUMBER(19,0) DEFAULT ‘0’ NOT NULL,
3. NAME VARCHAR2(255) NOT NULL,
4. DESCRIPTION CLOB
5. );
6. ALTER TABLE DEPARTMENT ADD CONSTRAINT PRIMARY_0 PRIMARY KEY(ID) ENABLE;
7. ALTER TABLE DEPARTMENT ADD CONSTRAINT UK_DEPARTMENT_1 UNIQUE (NAME);
8.
9. CREATE SEQUENCE DEPARTMENT_ID_SEQ MINVALUE 10000 MAXVALUE 999999999999999999999999 INCREMENT BY 1 NOCYCLE;
复制代码
创建DEPARTMENT表,并为DEPARTMENT表创建一个单独的SEQUENCE,名字为SEQUENCE_ID_SEQ,并不需要创建触发器。
[2]hibernate映射文件的配置:
Java代码
# 《?xml version=“1.0”?》
# 《!DOCTYPE hibernate-mapping PUBLIC
# “-//Hibernate/Hibernate Mapping DTD 3.0//EN”
# “http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd”》
# 《hibernate-mapping package=“com.liyanframework.demo.domain”》
# 《class name=“Department” table=“DEPARTMENT”》
# 《id name=“id” column=“ID”》
# 《generator class=“sequence”》
# 《param name=“sequence”》DEPARTMENT_ID_SEQ《/param》
# 《/generator》
# 《/id》
# 《property name=“name” column=“NAME” type=“string” /》
# 《property name=“description” column=“DESCRIPTION” type=“text” /》
# 《/class》
# 《/hibernate-mapping》
复制代码
在hibernate映射文件中,对ID的生成策略选择sequence,指定sequence的名字DEPARTMENT_ID_SEQ就可以了,当你保存新对象的时候,hibernate会自动取得DEPARTMENT_ID_SEQ.NEXTVAL作为新对象的ID保存到数据库,所以不需要再使用触发器再来生成新记录的ID。
Hibernate一对多关系的处理
一对多关系式Hibernate中的重要知识。感觉在Hibernate学习中最重要的也是最难的问题就是配置文件了,对于Hibernate一对多关系的配置***.hbm.xml现总结如下:
?xml version="1.0" encoding="gbk"? !DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" hibernate-mapping package="此处填写包文件" class name="一对多中一的实体类的类名" table="所对应的数据库的表名" id name="所对应的实体类的属性(id)" column="所对应的数据表的字段(**_id)" unsaved-value="null" generator class="native"/ /id property name="对应数据表中其他的字段" /property set name="对应实体类中的那个集合的属性" inverse="true" lazy="true" cascade="save-update" key column="对应其外键的字段 "/one-to-many class="对应一对多中多的实体类的类名"/ /set/classclass name="对应一对多中多的实体类的类名" table="相对应的数据表名" id name="id" column="对应表的主键字段名" unsaved-value="null" generator class="native"//idproperty name="对应数据表中的其他字段" /propertymany-to-one name="对应一对多中一的实体类的属性" column="对应实体类中的那个集合的属性" /many-to-one/class/hibernate-mapping |
Hibernate一对多关系的配置代码如下:
!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" hibernate-mapping package="entity.test" class name="Person" table="Person1" id name="pid" column="p_id" unsaved-value="null" generator class="native"/ /id property name="name" /property set name="computers" inverse="true" lazy="true" cascade="save-update" key column="p_id"/ one-to-many class="Computer"/ /set /class class name="Computer" table="Computer" id name="id" column="c_id" unsaved-value="null" generator class="native"/ /id property name="name" /property many-to-one name="person" column="p_id" /many-to-one /class /hibernate-mapping !DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" hibernate-mapping package="entity.test" class name="Person" table="Person1" id name="pid" column="p_id" unsaved-value="null" generator class="native"/ /id property name="name" /property set name="computers" inverse="true" lazy="true" cascade="save-update" key column="p_id"/ one-to-many class="Computer"/ /set /class class name="Computer" table="Computer" id name="id" column="c_id" unsaved-value="null" generator class="native"/ /id property name="name" /property many-to-one name="person" column="p_id" /many-to-one /class /hibernate-mapping |
整个的Hibernate技术就是围绕着配置文件去发展。
Hibernate批量更新与删除实例浅析
在我们的Java项目中,批量更新是指在一个事务中更新大批量数据,批量删除是指在一个事务中删除大批量数据。批量删除虽然在Hibernate里也可以实现,但因Hibernate的实现机制是一个一个删除,在数量大的情况下很影响效率;其实Hibernate提供的JDBC接口,可以方便的进行批量的更新和删除。
以下程序直接通过Hibernate API批量更新CUSTOMERS表中年龄大于零的所有记录的AGE字段:
如果CUSTOMERS表中有1万条年龄大于零的记录,那么Session的find()方法会一下子加载1万个Customer对象到内存。当执行tx.commit()方法时,会清理缓存,Hibernate执行1万条更新CUSTOMERS表的update语句:
以上批量更新方式有两个缺点:
(1)占用大量内存,必须把1万个Customer对象先加载到内存,然后一一更新它们。
(2)执行的update语句的数目太多,每个update语句只能更新一个Customer对象,必须通过1万条update语句才能更新一万个Customer对象,频繁的访问数据库,会大大降低应用的性能。
为了迅速释放1万个Customer对象占用的内存,可以在更新每个Customer对象后,就调用Session的evict()方法立即释放它的内存:
tx = session.beginTransaction(); Iterator customers=session.find("from Customer c where c.age>0").iterator(); while(customers.hasNext()){Customer customer=(Customer)customers.next(); customer.setAge(customer.getAge()+1); session.flush();session.evict(customer); } tx.commit();session.close(); |
在以上程序中,修改了一个Customer对象的age属性后,就立即调用Session的flush()方法和evict()方法,flush()方法使Hibernate立刻根据这个Customer对象的状态变化同步更新数据库,从而立即执行相关的update语句;evict()方法用于把这个Customer对象从缓存中清除出去,从而及时释放它占用的内存。
但evict()方法只能稍微提高批量操作的性能,因为不管有没有使用evict()方法,Hibernate都必须执行1万条update语句,才能更新1万个Customer对象,这是影响批量操作性能的重要因素。假如Hibernate能直接执行如下SQL语句:
update CUSTOMERS set AGE=AGE+1 where AGE>0; |
那么,以上一条update语句就能更新CUSTOMERS表中的1万条记录。但是Hibernate并没有直接提供执行这种update语句的接口。应用程序必须绕过Hibernate API,直接通过JDBC API来执行该SQL语句:
以上程序演示了绕过Hibernate API,直接通过JDBC API访问数据库的过程。应用程序通过Session的connection()方法获得该Session使用的数据库连接,然后通过它创建PreparedStatement对象并执行SQL语句。值得注意的是,应用程序仍然通过Hibernate的Transaction接口来声明事务边界。
如果底层数据库(如Oracle)支持存储过程,也可以通过存储过程来执行批量更新。存储过程直接在数据库中运行,速度更加快。在Oracle数据库中可以定义一个名为batchUpdateCustomer()的存储过程,代码如下:
以上存储过程有一个参数p_age,代表客户的年龄,应用程序可按照以下方式调用存储过程:
从上面程序看出,应用程序也必须绕过Hibernate API,直接通过JDBC API来调用存储过程。
Session的各种重载形式的update()方法都一次只能更新一个对象,而delete()方法的有些重载形式允许以HQL语句作为参数,例如:
session.delete("from Customer c where c.age>0"); |
如果CUSTOMERS表中有1万条年龄大于零的记录,那么以上代码能删除一万条记录。但是Session的delete()方法并没有执行以下delete语句:
delete from CUSTOMERS where AGE>0; |
Session的delete()方法先通过以下select语句把1万个Customer对象加载到内存中:
select * from CUSTOMERS where AGE>0; |
接下来执行一万条delete语句,逐个删除Customer对象:
由此可见,直接通过Hibernate API进行批量更新和批量删除都不值得推荐。而直接通过JDBC API执行相关的SQL语句或调用相关的存储过程,是批量更新和批量删除的最佳方式,这两种方式都有以下优点:
(1)无需把数据库中的大批量数据先加载到内存中,然后逐个更新或修改它们,因此不会消耗大量内存。
(2)能在一条SQL语句中更新或删除大批量的数据。
delete from CUSTOMERS where ID=i; delete from CUSTOMERS where ID=j; …… delete from CUSTOMERS where ID=k; |
tx = session.beginTransaction(); Connection con=session.connection(); String procedure = "{call batchUpdateCustomer(?) }"; CallableStatement cstmt = con.prepareCall(procedure);cstmt.setInt(1,0); //把年龄参数设为0cstmt.executeUpdate(); tx.commit(); |
create or replace procedure batchUpdateCustomer(p_age in number) asbeginupdate CUSTOMERS set AGE=AGE+1 where AGE>p_age; end; |
tx = session.beginTransaction(); Connection con=session.connection(); PreparedStatement stmt=con.prepareStatement("update CUSTOMERS set AGE=AGE+1 "+"where AGE>0 ");stmt.executeUpdate(); tx.commit(); |
update CUSTOMERS set AGE=? …. where ID=i; update CUSTOMERS set AGE=? …. where ID=j; …… update CUSTOMERS set AGE=? …. where ID=k; |
tx = session.beginTransaction(); Iterator customers=session.find("from Customer c where c.age>0").iterator(); while(customers.hasNext()){Customer customer=(Customer)customers.next(); customer.setAge(customer.getAge()+1); } tx.commit(); session.close(); |
Hibernate 3.5.0-CR-2发布
该版本主要是一些bugfix和文档的修正。在接下来的两周时间内,Hibernate主要工作还是对文档进行完善以及来自社区的bug报告,接下来还将发布一个 CR3 版本后,正式版本也将随即发布。
下载地址:http://sourceforge.net/projects/hibernate/files/hibernate3/
hibernate二级缓存
二级缓存称为进程级缓存或SessionFactory级缓存,它可以被所有session共享,它的生命周期伴随着SessionFactory的生命周期存在和消亡。
第一步:复制ehcache.xml文件到src目录下,配置二级缓存
<defaultCache
maxElementsInMemory="100"
eternal="false"
timeToIdleSeconds和timeToLiveSeconds就没有意义了
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskPersistent="true"
overflowToDisk="true"
/>
也可以配置某个特定对象的,如(但一般情况是使用缺省的就可以了):
<cache name="uuser"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
第二步:启用二级缓存
1.启用二级缓存,默认是启用的:
<property name="cache.use_second_level_cache">true</property>
2.设置缓存产品,如:
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
3.指定哪些类使用二级缓存(两种方法):
a.在hibernate.cfg.xml配置文件中定义
如:<class-cache class="lovo.po.User" usage="read-write"/>
b.在映射文件中定义
如:在<class>节点下:<cache usage="read-only"/>
有些类设置二级缓存,有些类不设置二级缓存;所以如果在映射文件中定义二级缓存的话会导致<cache>标签遍布在文件中,
因此常用第一种,即在hibernate.cfg.xml配置文件中定义,这样便于维护和管理。
第三步:二级缓存实例
1.打开两次session,调用load查询
代码部分:
Session session = this.sf.openSession();
User u1 = (User) session.load(User.class, 1);
System.out.println("用户姓名:"+u1.getName());
//关闭seeion,释放一级缓存
session.close();
//重新开启session
session = this.sf.openSession();
User u = (User) session.load(User.class, 1);
System.out.println("用户姓名:"+u.getName());
session.close();
执行部分:
如果设置<property name="cache.use_second_level_cache">false</property>,执行结果如下:
Hibernate:
select
user0_.id as id0_0_,
user0_.name as name0_0_,
user0_.password as password0_0_,
user0_.createTime as createTime0_0_,
user0_.expireTime as expireTime0_0_
from
t_user user0_
where
user0_.id=?
用户姓名:wangwu
Hibernate:
select
user0_.id as id0_0_,
user0_.name as name0_0_,
user0_.password as password0_0_,
user0_.createTime as createTime0_0_,
user0_.expireTime as expireTime0_0_
from
t_user user0_
where
user0_.id=?
用户姓名:wangwu
如果设置<property name="cache.use_second_level_cache">true</property>,执行结果如下:
Hibernate:
select
user0_.id as id0_0_,
user0_.name as name0_0_,
user0_.password as password0_0_,
user0_.createTime as createTime0_0_,
user0_.expireTime as expireTime0_0_
from
t_user user0_
where
user0_.id=?
用户姓名:wangwu
用户姓名:wangwu
可见,二级缓存是起作用了的。用get测试是同样的结果
2.在1的代码中加入this.sf.evict(User.class),清除二级缓存中的对象,执行结果就跟设置了<property name="cache.use_second_level_cache">false</property>的效果相同。
当然也可以指定清除哪些对象:this.sf.evict(User.class,1),结果很简单。
3.一级缓存和二级缓存的交互(CacheMode:CacheMode.NORMAL、CacheMode.PUT、CacheMode.GET)
代码部分:
Session session = this.sf.openSession();
//CacheMode.GET只向二级缓存中读数据不写
session.setCacheMode(CacheMode.GET);
User u1 = (User) session.load(User.class, 1);
//代码1和代码2顺序不能颠倒,否则CacheMode.GET对1号用户不起作用
System.out.println("用户姓名:"+u1.getName());
session.close();
//重新开启session
session = this.sf.openSession();
User u = (User) session.load(User.class, 1);
System.out.println("用户姓名:"+u.getName());
session.close();
执行结果:
Hibernate:
select
user0_.id as id0_0_,
user0_.name as name0_0_,
user0_.password as password0_0_,
user0_.createTime as createTime0_0_,
user0_.expireTime as expireTime0_0_
from
t_user user0_
where
user0_.id=?
用户姓名:wangwu
Hibernate:
select
user0_.id as id0_0_,
user0_.name as name0_0_,
user0_.password as password0_0_,
user0_.createTime as createTime0_0_,
user0_.expireTime as expireTime0_0_
from
t_user user0_
where
user0_.id=?
用户姓名:wangwu
默认情况下又读又写,即为CacheMode.NORMAL
CacheMode.PUT只写不读
hibernate一级缓存
在hibernate中,由于一级缓存的生命周期跟Session的生命周期一样,所以也可以理解为一级缓存是session缓存。
1、load()方法。
例子:
2System.out.println(s.getName());
3System.out.println("---------------------");
4
5System.out.println(s.getName());
PS:只发出一条sql语句,虽然这里使用两次load方法,但是第一次load方法对于id为1的记录进行加载,到第3行才发出一条sql语句及符合的数据,这样就把数据放在一级缓存里面,在第4行开始继续使用load就从一级缓存里面抽取。
2、get()方法。
例子:
2
3
4
5
PS:跟1中load一样,只发出一条sql语句。
3、iterate()方法查询实体对象
例子:
2
3
4
5
6
PS:总共发出3条sql语句,1、2行发出两条语句,1条是查询实体对象的sql,另一条是查询实体对象的name属性,由于使用一级缓存,之前1、2行查询的实体存放在一级缓存里面,所以5、6行利用一级缓存里面的数据只发出一条查询id的sql。
4、iterate()方法查询实体对象属性(不支持一级缓存)
例子:
2
3
4
5
6
7
PS:由于iterate()方法查询实体对象属性,一级缓存不会产生作用,所以发出两条sql语句。
例子:
10
11
PS:在数据量比较大的情况下管理一级缓存的做法,一般都是设定一定数量的记录给更新或者保存等操作之后,避免一次性大量的实体数据入库导致内存溢出,所以才去先是用第8行的flush和第9行的clear方法来实现比较好的缓存管理。在数据量特别大的时候,可以使用jdbc来实现,因为 hibernate不太适合于数据量特别大的场合使用,如果连jdbc都满足不了数据量的效率要求,只好利用相关的数据库机制来实现。
Hibernate分页的设计实现
使用Hibernate,在进行查询分页的操作上,是具有非常大的灵活性,Hibernate会首先尝试用特定数据库的分页sql,如果没用,再尝试Scrollable,如果不行,最后采用rset.next()移动的办法。
Hibernate 可以实现分页查询,例如:
从第2万条开始取出100条记录
Query q = session.createQuery("from Cat as c");
q.setFirstResult(20000);
q.setMaxResults(100);
List l = q.list();
那么Hibernate底层如何实现分页的呢?实际上Hibernate的查询定义在net.sf.hibernate.loader.Loader这个类里面,仔细阅读该类代码,就可以把问题彻底搞清楚。
Hibernate2.0.3的Loader源代码第480行以下:
if (useLimit) sql = dialect.getLimitString(sql);
PreparedStatement st = session.getBatcher().prepareQueryStatement(sql, scrollable);
如果相应的数据库定义了限定查询记录的sql语句,那么直接使用特定数据库的sql语句。
然后来看net.sf.hibernate.dialect.MySQLDialect:
public boolean supportsLimit() {
return true;
}
public String getLimitString(String sql) {
StringBuffer pagingSelect = new StringBuffer(100);
pagingSelect.append(sql);
pagingSelect.append(" limit ?, ?");
return pagingSelect.toString();
}
这是MySQL的专用分页语句,再来看net.sf.hibernate.dialect.Oracle9Dialect:
public boolean supportsLimit() {
return true;
}
public String getLimitString(String sql) {
StringBuffer pagingSelect = new StringBuffer(100);
pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
pagingSelect.append(sql);
pagingSelect.append(" ) row_ where rownum <= ?) where rownum_ > ?");
return pagingSelect.toString();
}
Oracle采用嵌套3层的查询语句结合rownum来实现分页,这在Oracle上是最快的方式,如果只是一层或者两层的查询语句的rownum不能支持order by。
除此之外,Interbase,PostgreSQL,HSQL也支持分页的sql语句,在相应的Dialect里面,大家自行参考。
如果数据库不支持分页的SQL语句,那么根据在配置文件里面
#hibernate.jdbc.use_scrollable_resultset true
默认是true,如果你不指定为false,那么Hibernate会使用JDBC2.0的scrollable result来实现分页,看Loader第430行以下:
if ( session.getFactory().useScrollableResultSets() ) {
// we can go straight to the first required row
rs.absolute(firstRow);
}
else {
// we need to step through the rows one row at a time (slow)
for ( int m=0; m<firstRow; m++ ) rs.next();
}
如果支持scrollable result,使用ResultSet的absolute方法直接移到查询起点,如果不支持的话,使用循环语句,rs.next一点点的移过去。
可见使用Hibernate,在进行查询分页的操作上,是具有非常大的灵活性,Hibernate会首先尝试用特定数据库的分页sql,如果没用,再尝试Scrollable,如果不行,最后采用rset.next()移动的办法。
在查询分页代码中使用Hibernate的一大好处是,既兼顾了查询分页的性能,同时又保证了代码在不同的数据库之间的可移植性。
- 默认分类(20)
- J2EE(25)
- Java(56)
- PHP(55)
- SEO(10)
- 网页设计(20)
- 网站建设(37)
- 数据库(7)
- JavaScript(17)
- JQuery(6)
- MySQL(20)
- SQL Server(6)
- Access(1)
- Oracle(6)
- office(6)
- Dreamweaver(4)
- Photoshop(12)
- Flash(9)
- Fireworks(13)
- CSS(14)
- HTML(4)
- .NET(7)
- ASP(2)
- DB2(1)
- Ajax(2)
- Linux(12)
- Struts(7)
- Hibernate(8)
- Spring(2)
- Jsp(22)
- Asp(8)
- C#(3)
- C++(1)
- 网络安全(5)
- 软件工程(7)
- XML(1)
- English(2)
- 计算机等级考试(2)
- 计算机病毒(4)
- 个人日志(76)
- 互联网(15)
- ActionScript(10)
- Android(3)
- 数据结构与算法(1)
- 游戏策略(3)
- 美文翻译(2)
- 编程开发(19)
- 计算机应用(4)
- 计算机(10)
- Unity3d(6)
- 其他(1)
- egret(1)