Hibernate 3.5.5下载地址:https://sourceforge.net/projects/hibernate/files/hibernate3/3.5.5-Final
1整合Hibernate,struts2,spring2(Dao,Service,Action三层架构)
Hibernate 3.5.5 + struts 2.1.8 + Spring 2.5.6 + apache-tomcat-7.0.22需要的包:
首先是struts 2.1.8需要的包,拷贝到WEB-INF/lib目录下
struts2-core-2.1.8.1.jar和xwork-core-2.1.6.jar,commons-logging-1.0.4.jar,freemarker-2.3.15.jar,ognl-2.7.3.jar,commons-fileupload-1.2.1.jar,commons-io-1.3.2.jar
然后是Spring 2.5.6与Struts 2.1.8整合需要的包以及使用Spring需要的包,拷贝到WEB-INF/lib目录下
struts2-spring-plugin-2.1.8.1.jar,spring.jar,log4j-1.2.15.jar,aspectjweaver.jar
Hibernate 3.5.5需要的包,以及spring3,struts2整合需要的包:
首先是Hibernate 3.5.5解压后的lib\required下的所有包:antlr-2.7.6.jar,commons-collections-3.1.jar,dom4j-1.6.1.jar,javassist-3.9.0.GA.jar,jta-1.1.jar,slf4j-api-1.5.8.jar并将commons-collections-3.1.jar替换为struts2中的commons-collections-3.2.jar。
另外还需要的包:hibernate3.jar,slf4j-nop-1.5.8.jar,hibernate-jpa-2.0-api-1.0.0.Final.jar,backport-util-concurrent-2.1.jar,ehcache-1.5.0.jar
还有我们在连接数据库使用的c3p0数据库连接池以及相应的数据库jdbc驱动包:c3p0-0.9.1.2.jar,mysql-connector-java-5.1.13-bin.jar
还需要将tomcat的lib下的servlet-api.jar和jsp-api.jar拷贝过来,当使用servlet和jsp时使用javax.servlet下面的类时需要这两个jar包。
以上是需要的包,下面打开web.xml在其中添加对Spring和struts的配置:
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
org.springframework.web.context.ContextLoaderListener
SetCharacterEncoding
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
SetCharacterEncoding
/*
FORWARD
REQUEST
INCLUDE
struts2
org.apache.struts2.dispatcher.FilterDispatcher
struts2
/*
log4jRefreshInterval
60000
log4jConfigLocation
/WEB-INF/conf/log/log4j.properties
org.springframework.web.util.Log4jConfigListener
org.springframework.web.util.IntrospectorCleanupListener
30
IntrospectorCleanupListener介绍:
spring中的提供了一个名为org.springframework.web.util.IntrospectorCleanupListener的监听器。它主要负责处理由
JavaBeans Introspector(内省)的使用而引起的缓冲泄露。spring中对它的描述如下:
如果有的框架或者程序用到了JavaBeans Introspector了,那么就启用了一个系统级别的缓存,这个缓存会存放一些曾加载并分析过的javabean的引用,当web服务器关闭的时候,由于这个缓存中存放着这些javabean的引用,所以垃圾回收器不能对web容器中的javaBean对象进行回收,导致内存越来越大。
spring不会出现这种问题,因为spring在加载并分析完一个类之后会马上刷新JavaBeans Introspector缓存,这样就保证了spring不会出现这种内存泄漏的问题。
但是有很多程序和框架在使用了JavaBeans Introspector之后,都没有进行清理工作,比如quartz、struts;解决办法很简单,就是上面的那个配置。
Struts2的Action类配置在struts.xml文件中,struts.xml文件应该放在WEB-INF/classess目录下:
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
/index.jsp
/error.jsp
上面的配置文件中对应的Action类:
package lifecenter.actions.product;
//使用Spring的Controller()注解Action类,并且必须是prototype范围的。
@Controller()
@Scope("prototype")
public class TestAction {
private String username;
private String password;
//由spring注入service层的类
private IRoleService roleService;
public IRoleService getRoleService() {
return roleService;
}
public void setRoleService(IRoleService roleService) {
this.roleService = roleService;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String login(){
// 如果用户名为scott,密码为tiger,则登录成功
if (getUsername().equals("scott") && getPassword().equals("tiger")) {
return "success";
} else {
return "error";
}
}
}
到此完成了Spring与struts的整合,
元素的class属性可以直接书写类的名称(注意类名的第一个字母换成小写否则会报错)而不用将包名.类名写全。Spring会自动根据我们提供的类名将相应的类注入进来。
在WEB-INF下新建applicationContext.xml文件,在其中完成Spring的配置已经Spring和Hibernate的整合:
首先配置数据源dataSource,使用C3P0数据库连接池实现。
然后配置Hibernate的会话工厂sessionFactory,会话工厂类用Spring提供的LocalSessionFactoryBean维护,
它注入了数据源和资源映射文件,此外还通过一些键值对设置了Hibernate所需的属性。
然后是创建事务管理器,配置Hibernate的局部事务管理器,使用HibernateTransactionManager类
该类实现PlatformTransactionManager接口,是针对Hibernate的特定实现。注意不是使用的DataSourceTransactionManager类。
在使用Spring的声明式事务,配置事务通知和切入点。
最后配置Spring的自动注入。以下是整合好的applicationContext.xml文件的内容:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
default-autowire="byType">
destroy-method="close">
${jdbc.driverClassName}
${jdbc.url}
${jdbc.username}
${jdbc.password}
5
30
10
60
5
0
60
30
true
false
${datasource.dialect}
true
create
org.hibernate.cache.EhCacheProvider
true
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
WEB-INF/conf/database/jdbc.properties
advice-ref="txAdvice" />
WEB-INF/conf/database/jdbc.properties文件:
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://127.0.0.1:3306/lifecenter_product
jdbc.username = root
jdbc.password =admin
datasource.dialect =org.hibernate.dialect.MySQL5InnoDBDialect
@SuppressWarnings("serial")
@Entity
@Table(name="users")
public class User implements Serializable{
//用户ID
@Id
@Column(name="id",length=32)
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "uuid")
private String userID;
//用户名称
@Column(nullable=false,length=32)
private String name;
@Temporal(TemporalType.TIMESTAMP)
@Column(nullable=true)
private Date createDate;
//用户密码
@Column(nullable=false,length=32)
private String password;
public String getUserID() {
return userID;
}
public void setUserID(String userID) {
this.userID = userID;
}
//....set/get方法
}
上面在配置hibernate的SessionFactory中,有下面的两段,表示那些被注解的实体类,这些实体类从包名导类名放在 中,hibernate将根据这些注解的实体类在数据库中建立对应的表。我们把要生成表的实体类一个一个的写在 中,这样比较麻烦,用可以直接让hibernate扫描我们给出包,hibernate将根据我们给出的包下的实体类来生成表,这样更简单,有多个包时用逗号隔开,如:lifecenter.manage.model.*或lifecenter.manage.model。
像上面Menu.java这样的实体Bean,如何注解使其建立对应的表呢,这里不再举例,因为:
Hibernate对实体类映射是采用的是注解方式,其实就是JPA的注解。和EJB3几乎一样。
上面SSH整合后,struts的Action类只多了@Controller()和@Scope("prototype")注解,Service层只多了@Service注解,Dao层多了@Repository注解,在Spring与ibatis结合时,Dao的类都继承自SqlMapClientDaoSupport,它是Spring中面向ibatis的辅助类。然后在Dao类中使用getSqlMapClientTemplate()得到SqlMapClientTemplate实例,通过SqlMapClientTemplate实例来完成对数据库的增删改查操作。
同样针对Spring与hibernate结合,HibernateDaoSupport是Spring对Hibernate的支持。所以SSH中DAO同样继承HibernateDaoSupport后,可以直接使用getHibernateTemplate()获取。HibernateTemplate将session中的大部分方法进行了封装,提供了更便捷好用的接口。下面就把HibernateTemplate的一些接口的使用解释一下。以下几乎所有的方法当发生Hibernate异常时都会抛出DataAccessException。
spring从2.5版本起,不建议使用hibernateTemplate,直接使用sessionFactory.getCurrentSession操作。
使用HibernateTemplate时,DAO的编写需要继承HibernateDaoSupport类:
@Repository
public class RoleDao extends HibernateDaoSupport implements IRoleDao{
public String addOne(Role role){
//使用HibernateTemplate实例的save方法向数据库中添加一条记录
getHibernateTemplate().save(role);
return role.getRoleID();
}
}
上面使用HibernateTemplate时,DAO的编写需要继承HibernateDaoSupport类,我们可以不继承它,但需要在ApplicationContext.xml文件中进行配置,并在Dao中自己注入HibernateTemplate实例。
//sessionFactory就是在上面的applicationContext中配置的sessionFactory
对应DAO的编写:
@Repository
public class RoleDao implements IRoleDao{
//手动注入HibernateTemplate实例
@Resource(name="hibernateTemplate")
HibernateTemplate hibernateTemplate;
public String addOne(Role role){
//使用HibernateTemplate实例的save方法向数据库中添加一条记录
getHibernateTemplate().save(role);
return role.getRoleID();
}
}
不使用HibernateTemplate时,DAO的编写:
@Repository
public class RoleDao implements IRoleDao{
//注入SessionFactory
@Resource(name="sessionFactory")
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public String addOne(Role role){
//获取Session实例并调用persist方法向数据库添加一条记录
Session session=sessionFactory.getCurrentSession();
session.persist(role);
return role.getRoleID();
}
}
在DAO层使用HibernateTemplate做CURD操作时。由于Spring进行了事务处理,异常不能在Dao内部捕获(在dao内部是捕获不到的),而只能在Service层外部捕获。
一般DAO中不捕捉异常也不抛出异常。
至于Service层和Action层,是在service层抛出CheckException然后由action捕捉,还是在service中全部采用unCheckException,然后在action中有选择的捕捉或者都不捕捉就要看你自己的选择了。
当发生DataAccessException时,在dao层与service是捕获不到的,因为被Spring处理了,只能在service外部的action层捕获到。可以看下面的Aop 异常通知来处理SSH框架的异常。
2HibernateDaoSupport和HibernateTemplate
HibernateTemplate提供非常多的常用方法来完成基本的操作,比如通常的增加、删除、修改、查询等操作。
void delete(Object entity):删除指定持久化实例
void deleteAll(Collection entities):删除集合内全部持久化类实例
List find(String queryString):根据HQL查询字符串来返回实例集合
示例:this.getHibernateTemplate().find("from bean.User");
List find(String queryString , Object value);
示例:this.getHibernateTemplate().find("from bean.User u where u.name=?", "test");
或模糊查询:this.getHibernateTemplate().find("from bean.User u where u.name like ?", "%test%");
List find(String queryString, Object[] values); Object[] values参数与查询语句的?占位符对应
示例:String hql= "from bean.User u where u.name=? and u.password=?"
this.getHibernateTemplate().find(hql, new String[]{"test", "123"});
findByNamedParam(String queryString , String paramName , Object value)
示例:
String queryString = "select count(*) from bean.User u where u.name=:myName";
String paramName= "myName";
String value= "xiyue";
this.getHibernateTemplate().findByNamedParam(queryString, paramName, value);
findByNamedParam(String queryString , String[] paramName , Object[] values)
示例:values与paramName一一对应
String queryString = "select count(*) from bean.User u where u.name=:myName and u.password=:myPassword";
String[] paramName= new String[]{"myName", "myPassword"};
String[] value= new String[]{"xiyue", "123"};
this.getHibernateTemplate().findByNamedParam(queryString, paramName, value);
List findByNamedQuery(String queryName):根据命名查询返回实例集合,queryName是命名查询的名称,参数value与上面的一样与查询语句的?和参数名称一一对应
List findByNamedQuery(String queryName, Object value)
List findByNamedQuery(String queryName, Object[] value)
List findByNamedQueryAndNamedParam(String queryName, String paramName, Object value)
List findByNamedQueryAndNamedParam(String queryName, String[] paramName, Object[] value)
Object get(Class entityClass, Serializable id):根据主键加载特定持久化类的实例
Serializable save(Object entity):保存新的实例
persist() 方法:这个方法基本和save() 方法的用法一样。
void saveOrUpdate(Object entity):根据实例状态,选择保存或者更新
merge() 方法:这个方法基本和saveOrUpdate() 方法的用法相似。
void update(Object entity):更新实例的状态,要求entity是持久状态
void setMaxResults(int maxResults):设置分页的大小
Object execute(HibernateCallback action):通过HibernateCallback访问数据库
List executeFind(HibernateCallback action)
可以考虑用标准的Hibernate3的编码方式作为HibernateTemplate的替代,因为Hibernate3提供同的SessionFactory.getCurrentSesion()已经取代了以往的那种每次操作都open一个新的Session的方式,同时Spring的LocalSessionFactoryBean自动支持Hibernate3的getCurrentSession的实录管理,也就是说不用HibernateTemplate而只用Hibernate3我们一样受用Spring的事务管理。
HibernateDaoSupport类
Spring为Hibernate的DAO提供工具类:HibernateDaoSupport。该类主要提供如下两个方法,方便DAO的实现:
public final HibernateTemplate getHibernateTemplate()
public final Session getSession()
public final void setSessionFactory(SessionFactory sessionFactory)
其中,setSessionFactory方法用来接收Spring的ApplicationContext的依赖注入,可接收配置在Spring的SessionFactory实例,getHibernateTemplate方法则用来根据刚才的SessionFactory产生Session,最后生成HibernateTemplate来完成数据库访问。
getSession()这个方法本身其实返回的是与当前事务绑定的Session对象,在HibernateDaoSupport中使用,HibernateDaoSupport本身是不负责对这个Session对象进行关闭的,所以在其中有一个对应的releaseSession()方法,用于关闭Session。
Spring+hibernate时,我们会采用HibernateTransactionManager进行事务管理,把事务配置在Service层。此时,它会帮助我们关闭与当前事务绑定的Session对象。
分页查询:
public List getListForPage(final String hql, final int offset,
final int length) {
List list = getHibernateTemplate().executeFind(new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException, SQLException {
Query query = session.createQuery(hql);
query.setFirstResult(offset);////offset=(pagenumber-1)*pageSize,offset=0表示第一条记录
query.setMaxResults(length);
List list = query.list();
return list;
}
});
return list;
}
3SessionFactory和Session
SessionFactory接口负责初始化Hibernate,它充当数据存储源的代理,并负责创建Session对象,SessionFactory并不是轻量级的,因为一般情况下一个项目通常只需一个SessionFactory就够了,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。
在SSH中Spring通过ApplicationContext管理SessionFactory 无须采用单独Hiberate 应用必需的hibernate.cfg.xml文件。
SessionFactory与数据库的连接,都由Spring的配置管理。实际的J2EE应用通常使用数据源,数据源会被Spring以依赖注入的方法传给Hibernate的SessionFactory所在上面看到配置中有 。
SessionFactory有ApplicationContext管理,会随着应用启动的时候加载,SessionFactory可以被处于ApplicationContext管理的任意一个Bean引用,比如DAO。
在Dao类中我们通过注解获得SessionFactory对象。
@Resource(name="sessionFactory")
private SessionFactory sessionFactory;
Spring还提供Hibernate 访问与其他持久层访问的一致性。如果需要使用容器管理的数据源(JTA),则无须提供数据驱动等信息,只需要提供数据源的JNDI 即可。对上文的SessionFactory 无须任何修改,只需将dataSource 的配置替换成JNDI 数据源,将原有的dataSource Bean 替换成如下所示:
java:comp/env/jdbc/myds
Session
Hibernate对资料库进行操作之前,必须先得到Session实例,相当于JDBC在对数据库操作之前,必须先去的Connection实例,Session是Hibernate操作的基础,它不是线程安全的,一个Session由一个线程来使用。多个线程同时使用一个Session实例进行数据存取则会导致Session数据存取逻辑混乱。
开启Session
Session实例由SessionFactory开启获取
//也可以使用sessionFactory.getCurrentSession();获得Session实例
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
....
tx.commit();
session.close();
开启Session不会马上取得Connection,而是在最后真正需要连接数据库进行更新或查询时才会取得Connection,如果有设定Connection pool,则从Connection pool中取得Connection,而关闭Session时,如果有设定Connection pool,则是将Connection归还给Connection pool,而不是直接关闭Connection。
通过Session可以对数据库进行新增,删除,更新,查询操作。Session实例的常用方法:
Transaction beginTransaction()
开始一个工作单元并返回一个与之相关Transaction对象,最为事务的开始,通常在需要对数据库进行更改例如save,update或delete时使用。在事务结束后需要调用Transaction的commit方法提交更改,该方法必须在Hibernate session关闭之前使用。
void flush()/void clear()
这两个方法通常在一起使用。由于Hibernate有缓存数据的功能,所以当我们要批量查询和批量插入时,会因为大量的缓存而导致内存溢出。所以我们可以在执行批量插入时插入一定数目的数据后调用flush()提交插入,然后调用clear()清空当前Hibernate session内的所有缓存。批量查询同理。调用session.flush()后就不能在继续用该session对象其执行查询,更新等操作。
session flush在commit之前默认都会执行他。也可以手动执行它,他主要做了两件事:
清理缓存。
执行SQL。
Connection close()
关闭Hibernate session,同时清空所有的缓存。
Connection connection()
获取当前Hibernate session使用的JDBC connection。
boolean contains(Object object)
返回boolean值,判断一个实例是否与当前Hibernate session保持关联,即为持久化状态。
Query createQuery(String queryString)
返回一个给定HQL语句的Query对象。
void cancelQuery ()
取消当前查询的执行。
boolean isOpen ()
检查会话仍然开启。
在Hibernate中使用的是org.hibernate.Query对象而EJB中使用的是javax.persistence.Query,org.hibernate.Query的常见方法:
int executeUpdate(),执行更新或删除语句,返回受影响的行数。
List list(),返回查询结果的List集合。
Object uniqueResult(),当查询结果只有一个的时候使用,有多个结果的时候使用List()。
Iterator iterate(),通过返回的Iterator来遍历查询结果集中元素。
Query setParameter(int,object);//同EJB的javax.persistence.Query的setParameter使用一样
Query setParameter(String,object);//同EJB的javax.persistence.Query的setParameter使用一样
当确定返回的实例只有一个或者null时 用uniqueResult()方法,其返回类型为Object。
Query query = session.createQuery("from Student s where s.id=?");
query.setString(0, "2");
Student student = (Student)query.uniqueResult();
SQLQuery createSQLQuery(String queryString)
返回一个用于执行原生SQL语句的SQLQuery对象
void delete(Object object)
当数据库中存在这条记录时,Hibernate 总会执行一条SQLDELETE 语句;当数据库中不存这条记录时,就会抛出异常
void delete(String entityName, Object object)
不太明白什么意思,占个位。
Connection disconnect()
断开与session与当前JDBC的连接,如果连接有Hibernate管理,则将连接送回连接池,否则将送回给程序本身。
Object get(Class clazz, Serializable id)
根据指定的实体类以及实体id返回一个实体的实例。如果找不到记录则返回null
Object get(String entityName, Serializable id)
同上,entity为实体的名字
String getEntityName(Object object)
返回一个持久化类的实体名
Serializable getIdentifier(Object object)
返回一个被session缓存的实体实例的id
Query getNamedQuery(String queryName)
返回一个在映射文件中定义的命名查询(包括注解和hbm.xml配置的命名查询)的query对象.
SessionFactory getSessionFactory()
获取生成当前session的SessionFactory
Transaction getTransaction()
不说了,地球人都知道
Object load(Class theClass, Serializable id)
Object load(String entityName, Serializable id)
和get方法一个效果,不同的是该方法在找不到对应记录时会抛出异常
Serializable save(Object object)
将一个实体实例持久化,返回该持久化实例的id。在持久化之前必须手动或自动的指派id。此操作级联到关联的实例,如果该协议cascade="save-update"
void persist ( Object object) 方法:
这个方法基本和save() 方法的用法一样。此操作级联到关联的实例,如果该协议是cascade="persist"
void update(Object object)
当数据库中存在这条记录时,Hibernate 总会执行一条SQLUPDATE 语句;当数据库中不存这条记录时,就会抛出异常。
此操作级联到关联的实例,如果该协议是cascade="save-update"。
void saveOrUpdate(Object object)
当数据库中没有这条有记录时,Hibernate 会执行一条SQLINSERT 语句。当数据库中有这条记录时,Hibernate的脏检查机制会自动检查游离对象的属性值与数据库中对应的字段值是否一样。如果一样就什么也不做;如果不一样就执行SQLUPDTE 语句。此操作级联到关联的实例,如果该协议是cascade="save-update"。
Object merge ( Object object) 方法:
复制到具有相同的持久对象给定对象的状态标识符。如果没有持久实例目前与会话相关联,它会被载入。返回值的持久化实例。如果给定的实例保存的,保存一个副本,并返回值一个新的持久化实例作为它。给出的实例不成为与会话。.此操作级联到关联的实例,如果该协议是cascade="merge"。
3以下整合过程可能出现的异常的解决办法:
java.lang.ClassNotFoundException: org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException
出现异常的原因是少了aspectjweaver.jar这个jar包
java.lang.NoClassDefFoundError: org/slf4j/impl/StaticLoggerBinder
出现异常的原因是少了slf4j-nop-1.5.8.jar这个jar包
java.lang.NoClassDefFoundError: javax/persistence/Entity
出现异常的原因是少了ejb3-persistence.jar这个jar包
java.lang.ClassNotFoundException: net.sf.ehcache.CacheException
出现异常的原因是少了ehcache-1.5.0.jar这个jar包
java.lang.NoClassDefFoundError: javax/persistence/Cacheable
javax.persistence.Cacheable 是 JPA 2.0 规范中的东西!加入hibernate-jpa-2.0-api-1.0.0.Final.jar
java.lang.ClassNotFoundException: edu.emory.mathcs.backport.java.util.concur
出现异常的原因是少了backport-util-concurrent-2.1.jar这个jar包
Error creating bean with name 'transactionManager'
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
应该写hibernate3,而不是hibernate
WARN:No configuration found. Configuring ehcache from ehcache-failsafe.xml found in the classpath: jar:file:/C:/Program%20Files/Tomcat%205.5/webapps/XFWebs/WEB-INF/lib/ehcache-1.2.4.jar!/ehcache-failsafe.xml 原因:报这个错是因为在/sandh/web-inf/classes下面没有ehcache.xml这个文件!这个文件是hibernate缓存配置文件。
解决方法:从ehcache-1.5.0.jar 中把文件ehcache-failsafe.xml 解压出来改名 ehcache.xml 复制到classes下面就行了!
配置文件解读:
表示当内存缓存中对象数量超过类设置内存缓存数量时,将缓存对象写到硬盘,path="java.io.tmpdir"表示把数据写到这个目录下。Java.io.tmpdir目录在运行时会根据相对路径生成。
表示设定缓存的默认数据过期策略。
表示设定用具体的命名缓存的数据过期策略。
name表示具体的缓存命名。
maxElementsInMemory表示cache中最大允许保存的对象数据量。
eternal表示cache中数据是否为常量。
timeToIdleSeconds表示缓存数据钝化时间
timeToLiveSeconds表示缓存数据的生命时间。
overflowToDisk表示内存不足时,是否启用磁盘缓存。
java.lang.NoSuchMethodError: javax.persistence.OneToMany.orphanRemoval()Z
如果是使用MyEclipse创建JavaEE5.0的Web项目, 解决办法:删除对Java EE 5 Libraries的使用。
问题原因:javaee.jar中的部分类与hibernate-jpa-2.0-api-1.0.0.Final.jar存在版本冲突。
如果使用了ejb的jar包,删除引用,例如ejb3-persistence.jar
问题原因:与hibernate-jpa-2.0-api-1.0.0.Final.jar冲突
如果是用junit测试,junit4自带的jpa包与hibernate的jpa包冲突
*************************
Hibernate中SessionFactory的getCurrentSession()与openSession()的区别?
getCurrentSession() 使用当前的 session
openSession() 重新建立一个新的 session
如果使用的是getCurrentSession来创建session的话,在commit后,session就自动被关闭了,也就是不用再session.close()了。但是如果使用的是openSession方法创建的session的话,那么必须显式的关闭session,也就是调用session.close()方法。这样commit后,session并没有关闭。
getCurrentSession创建的session会和绑定到当前线程,而openSession不会。getCurrentSession创建的线程会在事务回滚或事务提交后自动关闭,而openSession必须手动关闭,
使用getCurrentSession()需要在hibernate.cfg.xml文件中加入如下配置:
如果使用的是本地事务(jdbc事务)
thread
如果使用的是全局事务(jta事务)
jta
在 SessionFactory 启动的时候, Hibernate 会根据配置创建相应的 CurrentSessionContext ,在 getCurrentSession() 被调用的时候,实际被执行的方法是 CurrentSessionContext.currentSession() 。在 currentSession() 执行时,如果当前 Session 为空, currentSession 会调用 SessionFactory 的 openSession 。所以 getCurrentSession() 对于Java EE 来说是更好的获取 Session 的方法。
因为在HB3.X 版本中提供了一个getCurrentSession() 这个方法,第一次调用getCurrentSession() 的时候,其生命周期就开始。然后她被Hibernate 绑定到当前线程。当事务结束的时候,不管是提交还是回滚,Hibernate 会自动把Session 从当前线程剥离,并且关闭。若在次调用getCurrentSession() ,会得到一个新的Session, 并且开始一个新的工作单元。这是Hibernate 最广泛的thread-bound model(事物划分和数据访问代码的分离)
getCurrentSession和openSession无论是那种方式,如果是纯JDBC项目的话,那你必须手动写上事务的开启和提交。在上面的SSH配置由于我在Spring中配置有事务管理,所以我们用getCurrentSession时就不用手动写关于事务的代码(开始和提交)。
SSH中当Spring管理事务的时候不要在... 中配置thread 否则会出现异常,因为在spring的类LocalSessionFactoryBean源码,方法buildSessionFactory中将hibernate.current_session_context_class设为org.springframework.orm.hibernate3.SpringSessionContext所以我们不要再配置了,可以直接使用getCurrentSession()。
**********************
getHibernateTemplate().getSessionFactory().openSession()
getHibernateTemplate().getSessionFactory().getCurrentSession()
当hibernate与spring整合开发时,让DAO继承了spring的HibernateDaoSupport,这样的确能够提高开发效率 ,但是不够灵活,而且使DAO层依赖于spring的api,增加了耦合。但是不考虑复用的话还可以。下面一个一个的分析:
this.getsession实际上是调用了父类HibernateDaoSupport中的方法获得session。使用spring管理hibernate的SessionFactory的时候,这个方法会从session池中拿出一session.这样做有可能有问题,就是超session池连接数的时候,spring无法自动的关闭session。 不推荐使用
this.getHibernateTemplate().getSessionFactory().getCurrentSession()从spring管理的sessionFactory中创建一个绑定线程的session。spring会根据该线程的执行情况来自动判断是关闭session还是延迟关闭。这样做可以避免手动的管理事务,同时一个线程最多开启和关闭一次session又可以提高程序的性能。 极力推荐使用这种方法
this.getHibernateTemplate().getSessionFactory().OpenSession。这种方法从spring管理的sessionFactory中创建一个session,此session不是线程绑定的。当执行完一个事务的时候自动关闭session.这种方法不用手动管理事务,但是同一个线程多次的开启和关闭session,浪费系统资源和影响执行效率,正常情况下还是不要用了。
**********************
根据JPA对实体类进行CRUD(create,read,update,delete)的操作特点与Hibernate的处理方式做下对比
Hibernate处理方式为:通过SessionFactory获得session,然后通过session的各种方法进行CURD操作
JPA的处理方式为:通过EntityManagerFactory获得entityManager,然后通过entityManager的各种方法进行CURD
Hibernate是SessionFacotory sessionFactory=config.buidSessionFacotory()
JPA是EntityManagerFactory entityManagerFactory=Persistence.createEntityManagerFactory("xkkjpa");
hibernate是Session session=sessionFactory.openSession()
JPA是EntityManager entityManager=factory.createEntityManager()
session的增删改查为save,persist,delete,update,merge,createQuery()
entityManager的增删改为persist,remove,merge,createQuery
********************
在Hibernate中持久化类实例的3种状态:
临时状态
临时状态(transient)是指持久化类实例从通过new语句创建到被持久化之前,处于临时状态的持久化类实例简称“临时对象”。
持久化状态
持久化状态(persistent)是指持久化类实例从被持久化到Session实例被销毁,处于持久化状态的持久化类实例简称“持久化对象”。
游离状态
游离状态(detached)是指持久化类实例从关联它的Session实例被销毁到该实例消失,处于游离状态的持久化类实例简称“游离对象”。
**************
Aop 异常通知(处理框架的日志,异常)
上面整合Hibernate,struts2,spring2(Dao,Service,Action三层架构),我们在Service层(业务层)配置了事务控制,那么在Dao,Sevice层我们不能捕获到DataAccessException异常,因为它被Spring处理了,只能在Action层捕获它,我们可以使用Aop 异常通知,对Service层发生的异常进行统一的处理如:记录日志,统一抛出自定义的异常。下面是对异常通知的配置:
advice-ref="txAdvice" order="2"/>
LogAdvice类
@Component
public class LogAdvice {
private static Logger log;
public void saveLog(Exception dataAccessEx) throws LifecenterException{
log=Logger.getLogger(LogAdvice.class.getName());
//将异常信息记录在日志文件中。
log.error(getException(dataAccessEx)+"end\r\n\r\n");
System.out.println("advice Aspect************");
//打印出异常栈信息
dataAccessEx.printStackTrace();
//统一重新抛出自定义异常LifecenterException,这样发生异常(如DataAccessException等所有异常)的Service层的方法将抛出LifecenterException异常,那么在Action对这些方法捕获的就是LifecenterException异常,然后进行处理
throw new LifecenterException();
}
//该方法将异常dataAccessEx的printStackTrace()信息转化为字符串返回
public String getException(Exception e) {
StringBuffer bs = new StringBuffer();
StackTraceElement[] a = e.getStackTrace();
bs.append("\r\n start异常信息: " + e.fillInStackTrace() + "");
for (int i = 0; i < a.length; i++) {
bs.append("\r\n在" + a[i].getClassName() + "类(" + a[i].getFileName() + ":" + a[i].getLineNumber() + "行,"
+ a[i].getMethodName() + "()方法)");
}
return bs.toString();
}
}
这样在dao层与Service层就不需要try..catch捕获和处理异常了。交给异常通知统一进行日志记录,异常处理,抛出一个统一的自定义异常(LifecenterException),然后再Action直接捕获这个统一的自定义异常进行处理。异常通知的优先级高于声明式事务的优先级,我们先进想异常通知的处理然后再进行事务处理(事务回滚等)
下面是log4j.properties配置文件内容:
log4j.rootCategory=warn,console,R
##将异常日志输入到控制台和写入到lifecenter_product.log##
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d %p (%c\:%L)- %m%n
##RollingFileAppender表示当文件大小到达指定尺寸的时候产生一个新的文件##
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=${webapp.root}/logs/lifecenter_product.log
log4j.appender.R.MaxFileSize=5000KB
log4j.appender.R.MaxBackupIndex=2
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d %p (%c\:%L)- %m%n
自定义异常LifecenterException
public class LifecenterException extends Exception {
private static final long serialVersionUID = 1L;
public LifecenterException()
{
super();
}
public LifecenterException (String msg){
super(msg);
}
public LifecenterException(String msg, Throwable cause)
{
super(msg, cause);
}
public LifecenterException(Throwable cause){
super(cause);
}
}
****************
Hibernate SQL 方言 (hibernate.dialect)
RDBMS
方言
DB2
net.sf.hibernate.dialect.DB2Dialect
MySQL
net.sf.hibernate.dialect.MySQLDialect
SAP DB
net.sf.hibernate.dialect.SAPDBDialect
Oracle (所有版本)
net.sf.hibernate.dialect.OracleDialect
Oracle 9
net.sf.hibernate.dialect.Oracle9Dialect
Sybase
net.sf.hibernate.dialect.SybaseDialect
Sybase Anywhere
net.sf.hibernate.dialect.SybaseAnywhereDialect
Progress
net.sf.hibernate.dialect.ProgressDialect
Mckoi SQL
net.sf.hibernate.dialect.MckoiDialect
Interbase
net.sf.hibernate.dialect.InterbaseDialect
Pointbase
net.sf.hibernate.dialect.PointbaseDialect
PostgreSQL
net.sf.hibernate.dialect.PostgreSQLDialect
HypersonicSQL
net.sf.hibernate.dialect.HSQLDialect
Microsoft SQL Server
net.sf.hibernate.dialect.SybaseDialect
Ingres
net.sf.hibernate.dialect.IngresDialect
Informix
net.sf.hibernate.dialect.InformixDialect
FrontBase
net.sf.hibernate.dialect.FrontbaseDialect
********************
优化Hibernate,在配置文件中设置hibernate.jdbc.batch_size参数,来指定每次提交SQL的数量;程序上采用分段插入及时清除缓存的方法(Session实现了异步write-behind,它允许Hibernate显式地写操作的批处理),也就是每插入一定量的数据后及时的把它们从内部缓存中清除掉,释放占用的内存。
例如:
50
****************
由于spring对hibernate配置文件hibernate.cfg.xml的集成相当好,
所以,在项目中我一直使用spring的 org.springframework.orm.hibernate3.LocalSessionFactoryBean来取代 hibernate.cfg.xml文件的功能
LocalSessionFactoryBean有好几个属性用来查找hibernate映射文件:mappingResources、 mappingLocations、mappingDirectoryLocations与mappingJarLocations
他们的区别:
mappingResources:指定classpath下具体映射文件名
petclinic.hbm.xml
mappingLocations:可以指定任何文件路径,并且可以指定前缀:classpath、file等
/WEB-INF/petclinic.hbm.xml
classpath:/com/company/domain/petclinic.hbm.xml
也可以用通配符指定,'*'指定一个文件(路径)名,'**'指定多个文件(路径)名,例如:
classpath:/com/company/domainmaps/*.hbm.xml
上面的配置是在com/company/domain包下任何maps路径下的hbm.xml文件都被加载为映射文件
mappingDirectoryLocations:指定映射文件所在文件夹路径
WEB-INF/HibernateMappings
也可以通过classpath来指出
classpath:/XXX/package/
mappingJarLocations:指定加载的映射文件在jar文件中
*******************
/hbm/person.hbm.xml映射文件:
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
使用LocalSessionFactoryBean,根据hbm.xml文件自动创建数据表:
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
classpath:/hbm/
${datasource.dialect}
true
true
create
org.hibernate.cache.EhCacheProvider
true
***********************
AnnotationSessionFactoryBean类本身是继承自LocalSessionFactoryBean,LocalSessionFactoryBean支持通过hibernate映射文件hbm.xml进行配置;AnnotationSessionFactoryBean支持注解,其实它也支持通过hbml.xml进行配置。
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
classpath:/hbm/
lifecenter.manage.model.*
${datasource.dialect}
true
create
org.hibernate.cache.EhCacheProvider
true
********************
classpath 和 classpath* 区别:
classpath:只会到你的class路径中查找找文件;
classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找.
**************
在hibernate的映射文件中hbm.xml中,如果出现:
则会抛出String index out of range:0异常。
*****************
在hibernate自动创建表中,不要将model的字段名设置为read,否则不会自动创建表,也不会像表中级联插入更新数据。
***************
hibernate调用存储过程
如下实例:
public void saveStoredProcedure(final String adminId, final String menuId, final String toolbars) {
getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException, SQLException {
//通过call调用,?是传递给存储过程P_SaveOperatorPopedom的参数
SQLQuery sqlQuery = session.createSQLQuery("{call P_SaveOperatorPopedom(?,?,?)}");
sqlQuery.setString(0, adminId);
sqlQuery.setString(1, menuId);
sqlQuery.setString(2, toolbarsXML);
sqlQuery.executeUpdate();
return null;
}
});
}
也可以直接使用hibernate的session对象调用,觉得这种方式最简单:
Session session =sessionFactory.getCurrentSession();
Query query=session.createSQLQuery("{call pQY_repairType(:organId,:taskCode,:beginDate)}");
query.setString("organId","'1111','1112'");//存储过程参数将收到'1111','1112'
query.setString("taskCode",null);//存储过程参数将收到NULL
query.setString("beginDate","'2012-01-01'");//存储过程参数将收到'2012-01-01'
List resultList=query.list();
return resultList;
还可以使用CallableStatement
CallableStatement call = conn.prepareCall("{Call proc(?)}");
call.setString(1, 参数);
rs = call.executeQuery();
****************
在hibernate.cfg.xml文件里面加入c3p0的配置:
20
5
120
100
120
2
true
***************
1、a different object with the same identifier value was already associated with the session。
错误原因:在hibernate中同一个session里面有了两个相同标识但是是不同实体。
解决方法一:session.clear()。在session.saveOrUpdate()方法之前调用。
解决方法二:session.refresh(object)
PS:当object不是数据库中已有数据的对象的时候,不能使用session.refresh(object)因为该方法是从hibernate的session中去重新取object,如果session中没有这个对象,则会报错所以当你使用saveOrUpdate(object)之前还需要判断一下。
解决方法三:session.merge(object)
PS:Hibernate里面自带的方法。但是会MS sql不管用
使用merger,saveOrUpdate多插入了一个对象,最后使用clear()解决。
2、Found two representations of same collection
错误原因:见1。
解决方法:session.merge(object)
以上两中异常经常出现在一对多映射和多对多映射中
****************************
xml中配置
如果你用来配置JDBC URL的方法是基于XML的,需要使用XML字符“&”来隔开配置参数,“&”是XML的保留字符。
*****************************
tomcat中添加数据库连接池:
在$CATALINA_HOME/conf/server.xml或者应用中的/META-INF/context.xml中添加:
username="sa" password="sa"
driverClassName="com.microsoft.jdbc.sqlserver.SQLServerDriver"
url="jdbc:microsoft:sqlserver://localhost:1433;databasename=northwind"
maxActive="8" maxIdle="4"/>
这样一段数据源配置信息,选好driverClassName和赋值对url的值,以及其他的一些参数。
下面是参数的含义:
* driverClassName - 所使用的JDBC驱动类全称。
* maxActive - 同一时刻可以自数据库连接池中被分配的最大活动实例数。
* maxIdle - 同一时刻数据库连接池中处于非活动状态的最大连接数。
* maxWait - 当连接池中没有可用连接时,连接池在抛出异常前将等待的最大时间,单位毫秒。
* password - 传给JDBC驱动的数据库密码。
* url - 传给JDBC驱动的连接URL。
* user - 传给JDBC驱动的数据库用户名。
* validationQuery - 一个SQL查询语句,用于在连接被返回给应用前的连接池验证。如果指定了该属性,则必为至少返回一行记录的SQL SELECT语句。
必须在web.xml中声明资源:
这里我们为JDBC数据源建立的JNDI资源名为“jdbc/dataSource2005”,需要在web应用部署描述文件web.xml中添加资源声明:
"http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">
index.jsp
Database connection
jdbc/dataSource2005
javax.sql.DataSource
Container
这样就在tomcat配置了数据库连接。Jboss也可以进行如此操作。
上面的数据库连接池在hibernate.cfg.xml中使用:
java:comp/env/jdbc/dataSource2005