断断续续学习hibernate也有一段时间了,在这里研究一下SSH开发环境的搭建过程,自己简单的搭建一个SSH的开发环境。采用maven搭建。
0.项目结构:
1.导包:(maven项目)
pom.xml配置文件
4.0.0
cn.qlq
SSHWeb
0.0.1-SNAPSHOT
war
UTF-8
4.2.4.RELEASE
5.1.37
1.6.10
javax.servlet
servlet-api
2.5
provided
javax.servlet
jsp-api
2.0
provided
org.springframework
spring-core
${spring.version}
org.springframework
spring-aop
${spring.version}
org.springframework
spring-test
${spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-beans
${spring.version}
org.springframework
spring-context
${spring.version}
org.springframework
spring-aspects
${spring.version}
org.springframework
spring-orm
${spring.version}
org.springframework
spring-oxm
${spring.version}
org.springframework
spring-web
${spring.version}
mysql
mysql-connector-java
${mysql.version}
org.hibernate
hibernate-core
5.0.7.Final
c3p0
c3p0
0.9.1.2
org.apache.struts
struts2-convention-plugin
2.3.24
org.apache.struts
struts2-core
2.3.24
org.apache.struts
struts2-json-plugin
2.3.24
org.apache.struts
struts2-spring-plugin
2.3.24
org.slf4j
slf4j-api
1.7.25
org.slf4j
slf4j-log4j12
1.7.5
org.apache.logging.log4j
log4j-api
2.0-rc1
org.apache.logging.log4j
log4j-core
2.0-rc1
org.apache.maven.plugins
maven-compiler-plugin
3.5.1
1.7
1.7
UTF-8
org.apache.tomcat.maven
tomcat7-maven-plugin
2.2
80
/ssh
UTF-8
tomcat7
2.配置文件介绍:
1.db.properties和log4j.properties
hibernate.dialect=org.hibernate.dialect.MySQLDialect
driverClassName=com.mysql.jdbc.Driver
validationQuery=SELECT 1
url=jdbc:mysql://localhost:3306/hibernate?useUnicode=true&characterEncoding=UTF-8
username=sa
password=123456
hibernate.hbm2ddl.auto=update
hibernate.show_sql=true
hibernate.format_sql=true
log4j.rootLogger=debug,A,B
log4j.appender.A=org.apache.log4j.ConsoleAppender
log4j.appender.A.layout=org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
log4j.appender.B=org.apache.log4j.RollingFileAppender
log4j.appender.B.Threshold=info
log4j.appender.B.File=E:\\ssh.log
log4j.appender.B.MaxFileSize=10MB
log4j.appender.B.MaxBackupIndex=5
log4j.appender.B.layout=org.apache.log4j.PatternLayout
log4j.appender.B.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
1.spring相关配置文件:
spring.xml
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
spring-hibernate.xml
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
">
${hibernate.hbm2ddl.auto}
${hibernate.dialect}
${hibernate.show_sql}
${hibernate.format_sql}
2.struts.xml
配置一些常量,采用注解开发。
/p>
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
3.hibernate.cfg.xml
/p>
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
4.web.xml
spring监听器、配置文件路径、hibernate会话过滤器(一次请求前开启session,请求完成关闭session)、struts入口过滤器(注意hibernate打开会话过滤器必须配置在struts入口前面)
ssh
contextConfigLocation
classpath:spring.xml,classpath:spring-hibernate.xml
org.springframework.web.context.ContextLoaderListener
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
encodingFilter
/*
openSessionInView
org.springframework.orm.hibernate5.support.OpenSessionInViewFilter
openSessionInView
/*
struts2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
struts2
/*
index.jsp
3.接下来就是测试代码:
0.实体类以及xml配置文件
Customer.java
packagecn.qlq.domain;importjava.util.HashSet;importjava.util.Set;public classCustomer {privateLong cust_id;privateString cust_name;privateString cust_source;privateString cust_industry;privateString cust_level;privateString cust_linkman;privateString cust_phone;privateString cust_mobile;//使用set集合,表达一对多关系
private Set linkMens = new HashSet();public SetgetLinkMens() {returnlinkMens;
}public void setLinkMens(SetlinkMens) {this.linkMens =linkMens;
}publicLong getCust_id() {returncust_id;
}public voidsetCust_id(Long cust_id) {this.cust_id =cust_id;
}publicString getCust_name() {returncust_name;
}public voidsetCust_name(String cust_name) {this.cust_name =cust_name;
}publicString getCust_source() {returncust_source;
}public voidsetCust_source(String cust_source) {this.cust_source =cust_source;
}publicString getCust_industry() {returncust_industry;
}public voidsetCust_industry(String cust_industry) {this.cust_industry =cust_industry;
}publicString getCust_level() {returncust_level;
}public voidsetCust_level(String cust_level) {this.cust_level =cust_level;
}publicString getCust_linkman() {returncust_linkman;
}public voidsetCust_linkman(String cust_linkman) {this.cust_linkman =cust_linkman;
}publicString getCust_phone() {returncust_phone;
}public voidsetCust_phone(String cust_phone) {this.cust_phone =cust_phone;
}publicString getCust_mobile() {returncust_mobile;
}public voidsetCust_mobile(String cust_mobile) {this.cust_mobile =cust_mobile;
}
@OverridepublicString toString() {return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + "]";
}publicCustomer(Long cust_id, String cust_name) {super();this.cust_id =cust_id;this.cust_name =cust_name;
}publicCustomer() {
}
}
Customer.hbm.xml
/p>
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
LinkMan.java
packagecn.qlq.domain;//联系人实体
public classLinkMan {privateLong lkm_id;privateCharacter lkm_gender;privateString lkm_name;privateString lkm_phone;privateString lkm_email;privateString lkm_qq;privateString lkm_mobile;privateString lkm_memo;privateString lkm_position;//表达多对一关系
privateCustomer customer;publicCustomer getCustomer() {returncustomer;
}public voidsetCustomer(Customer customer) {this.customer =customer;
}publicLong getLkm_id() {returnlkm_id;
}public voidsetLkm_id(Long lkm_id) {this.lkm_id =lkm_id;
}publicCharacter getLkm_gender() {returnlkm_gender;
}public voidsetLkm_gender(Character lkm_gender) {this.lkm_gender =lkm_gender;
}publicString getLkm_name() {returnlkm_name;
}public voidsetLkm_name(String lkm_name) {this.lkm_name =lkm_name;
}publicString getLkm_phone() {returnlkm_phone;
}public voidsetLkm_phone(String lkm_phone) {this.lkm_phone =lkm_phone;
}publicString getLkm_email() {returnlkm_email;
}public voidsetLkm_email(String lkm_email) {this.lkm_email =lkm_email;
}publicString getLkm_qq() {returnlkm_qq;
}public voidsetLkm_qq(String lkm_qq) {this.lkm_qq =lkm_qq;
}publicString getLkm_mobile() {returnlkm_mobile;
}public voidsetLkm_mobile(String lkm_mobile) {this.lkm_mobile =lkm_mobile;
}publicString getLkm_memo() {returnlkm_memo;
}public voidsetLkm_memo(String lkm_memo) {this.lkm_memo =lkm_memo;
}publicString getLkm_position() {returnlkm_position;
}public voidsetLkm_position(String lkm_position) {this.lkm_position =lkm_position;
}
@OverridepublicString toString() {return "LinkMan [lkm_id=" + lkm_id + ", lkm_gender=" + lkm_gender + ", lkm_name=" + lkm_name + ", lkm_phone="
+ lkm_phone + ", lkm_email=" + lkm_email + ", lkm_qq=" + lkm_qq + ", lkm_mobile=" +lkm_mobile+ ", lkm_memo=" + lkm_memo + ", lkm_position=" + lkm_position + ", customer=" + customer + "]";
}
}
LinkMan.hbm.xml
/p>
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
1.dao接口以及实现
packagecn.qlq.dao;importorg.hibernate.Session;importcn.qlq.domain.Customer;public interfaceCustomerDao {/*** 保存客户
*
*@paramc*/
public voidsaveCustomer(Customer c);/*** 根据ID查询
*
*@paramcusId
*@return
*/
publicCustomer getCustomerById(Long cusId);/*** 测试通过hibernate获取的session是否同一线程是一样的*/
publicSession testSessionIsSameInOneThread();
}
packagecn.qlq.dao;importjava.io.Serializable;importjava.util.List;importjavax.annotation.Resource;importorg.hibernate.Criteria;importorg.hibernate.Query;importorg.hibernate.SQLQuery;importorg.hibernate.Session;importorg.hibernate.SessionFactory;importorg.hibernate.criterion.Restrictions;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.orm.hibernate5.support.HibernateDaoSupport;importorg.springframework.stereotype.Repository;importcn.qlq.domain.Customer;/*** dao操作一般继承HibernateDaoSupport,里面获取hibernateTemplete也可以进行许多操作
*
*@authorliqiang
**/@Repositorypublic class CustomerDaoImpl extends HibernateDaoSupport implementsCustomerDao {private static Logger log = LoggerFactory.getLogger(CustomerDaoImpl.class);
@Overridepublic voidsaveCustomer(Customer c) {
Serializable obj= getHibernateTemplate().save(c);//返回值是生成的主键的值
log.info("save customer success,userId is:{}", obj.toString());
}
@Resourcepublic voidsetSessionFacotry(SessionFactory sessionFacotry) {super.setSessionFactory(sessionFacotry);
}
@OverridepublicCustomer getCustomerById(Long cusId) {//第一种:session.get方法
Session session =getSessionFactory().openSession();
Customer customer= session.get(Customer.class, cusId);
log.info("第一种方法(session获取):{}", customer.toString());//第二种:HQL
String hql = "from Customer where cust_id=" +cusId;
Query query=session.createQuery(hql);
Customer cus=(Customer) query.uniqueResult();
log.info("第二种方法(HQL获取):{}", customer.toString());//第三种:Criteria查询
Criteria c = session.createCriteria(Customer.class);
c.add(Restrictions.eq("cust_name", "ttt"));
List list=c.list();
log.info("Criteria方法获取的:{}", list.toString());//第四种:原生SQL查询
String sql = "select * from cst_customer where cust_id = " +cusId;
SQLQuery sqlQuery=session.createSQLQuery(sql);
sqlQuery.addEntity(Customer.class);
log.info("原生SQL查询方法获取的:{}", sqlQuery.list().toString());returncustomer;
}
@OverridepublicSession testSessionIsSameInOneThread() {returngetHibernateTemplate().getSessionFactory().getCurrentSession();
}
}
dao实现一般要继承HibernateDaoSupport,我们项目中也是采用所有的Dao都继承HibernateDaoSupport。这个抽象类有好多直接可以用的方法。原理都是调用hibernateTemplate。所以在Dao实现层我们可以选择继承HibernateDaoSupport,或者直接注入HibernateTemplate。
也可以用HibernateTemplate进行操作(直接注入HibernateTemplate),此方法的实例我们已经在XML中注入,通过此对象可以获取session执行原生SQL,或者直接注入SessionFactory获取Session(不建议这种)。
2.service接口和实现
packagecn.qlq.service;importcn.qlq.domain.Customer;public interfaceCustomerService {public booleansaveCustomer(Customer c);/*** 根据ID查询
*
*@paramcusId
*@return
*/
publicCustomer getCustomerById(Long cusId);/*** 测试通过hibernateTemplete获取的session是否同一线程是一样的*/
public booleantestSessionIsSameInOneThread();
}
packagecn.qlq.service;importorg.hibernate.Session;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.orm.hibernate5.HibernateTemplate;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;importcn.qlq.dao.CustomerDao;importcn.qlq.domain.Customer;
@Service
@Transactionalpublic class CustomerServiceImpl implementsCustomerService {private static Logger log = LoggerFactory.getLogger(CustomerServiceImpl.class);
@AutowiredprivateCustomerDao customerDao;
@AutowiredprivateHibernateTemplate hibernateTemplate;
@Overridepublic booleansaveCustomer(Customer c) {
customerDao.saveCustomer(c);return true;
}
@OverridepublicCustomer getCustomerById(Long cusId) {returncustomerDao.getCustomerById(cusId);
}
@Overridepublic booleantestSessionIsSameInOneThread() {//getCurrentSession获取与线程绑定的session(返回true),而openSession不是同一个(会返回false)//Session serviceSession = hibernateTemplate.getSessionFactory().openSession();
Session serviceSession =hibernateTemplate.getSessionFactory().getCurrentSession();
log.info("serviceSession---------------{}", serviceSession.toString());
Session daoSession=customerDao.testSessionIsSameInOneThread();
log.info("daoSession---------------{}", daoSession.toString());
log.info("daoSession.equals(serviceSession) is :{}", daoSession.equals(serviceSession));returndaoSession.equals(serviceSession);
}
}
3.Action层代码
packagecn.qlq.action;importjava.util.HashMap;importjava.util.Map;importorg.apache.struts2.convention.annotation.Action;importorg.apache.struts2.convention.annotation.Namespace;importorg.apache.struts2.convention.annotation.ParentPackage;importorg.apache.struts2.convention.annotation.Result;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importcn.qlq.domain.Customer;importcn.qlq.service.CustomerService;/*** 客户Action
*
*@authorliqiang
**/@Namespace("/cus")
@ParentPackage("default")
@SuppressWarnings("all")public classCustomerAction {private static Logger log = LoggerFactory.getLogger(CustomerAction.class);
@AutowiredprivateCustomerService customerService;private Map responseMap = newHashMap();private Customer c;//对象驱动保存对象
@Action(value= "saveCus", results ={
@Result(name= "success", type = "json", params = { "root", "responseMap"}) })publicString saveCus() {try{
customerService.saveCustomer(c);
}catch(Exception e) {
responseMap.put("msg", "保存客户失败!");return "success";
}
responseMap.put("msg", "保存客户成功!");return "success";
}
@Action(value= "getCusById", results ={
@Result(name= "success", type = "json", params = { "root", "responseMap"}) })publicString getCusById() {
Customer cus= null;try{
cus= customerService.getCustomerById(15l);
}catch(Exception e) {
responseMap.put("msg", "查询客户失败!");return "success";
}
responseMap.put("msg", "查询客户成功!");
responseMap.put("data", cus);return "success";
}
@Action(value= "testSessionIsSameInOneThread", results ={
@Result(name= "success", type = "json", params = { "root", "responseMap"}) })publicString testSessionIsSameInOneThread() {
responseMap.put("data", customerService.testSessionIsSameInOneThread());return "success";
}publicCustomer getC() {returnc;
}public voidsetC(Customer c) {this.c =c;
}publicMap getResponseMap() {returnresponseMap;
}public voidsetResponseMap(Map responseMap) {this.responseMap =responseMap;
}
}
3.测试(就不搭建页面了,直接get请求访问根据返回的JSON值判断结果)
1.测试保存:
http://localhost/SSHWeb/cus/saveCus.do?c.cust_name=ttt
结果:
日志:
2018-08-25 20:42:19 [cn.qlq.dao.CustomerDaoImpl]-[INFO] save customer success,userId is:18
2.测试查询:
http://localhost/SSHWeb/cus/getCusById.do
结果:(会关联查询联系人)
日志:
2018-08-25 20:45:26 [cn.qlq.dao.CustomerDaoImpl]-[INFO] 第一种方法(session获取):Customer [cust_id=15, cust_name=ttt]
2018-08-25 20:45:27 [cn.qlq.dao.CustomerDaoImpl]-[INFO] 第二种方法(HQL获取):Customer [cust_id=15, cust_name=ttt]
2018-08-25 20:45:27 [cn.qlq.dao.CustomerDaoImpl]-[INFO] Criteria方法获取的:[Customer [cust_id=15, cust_name=ttt], Customer [cust_id=18, cust_name=ttt]]
2018-08-25 20:45:27 [cn.qlq.dao.CustomerDaoImpl]-[INFO] 原生SQL查询方法获取的:[Customer [cust_id=15, cust_name=ttt]]
3.测试获取与线程绑定的Session
http://localhost/SSHWeb/cus/testSessionIsSameInOneThread.do
结果:
日志:
2018-08-25 20:47:16 [cn.qlq.service.CustomerServiceImpl]-[INFO] serviceSession---------------SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])2018-08-25 20:47:16 [cn.qlq.service.CustomerServiceImpl]-[INFO] daoSession---------------SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])2018-08-25 20:47:16 [cn.qlq.service.CustomerServiceImpl]-[INFO] daoSession.equals(serviceSession) is :true
4.测试事务(一般我们都要在service层打事务注解进行事务控制)
上面的搭建环境基本完成了,但是事务控制非常重要,下面测试事务是否生效。
原来数据:
mysql> select * fromcst_customer;+---------+-----------+-------------+---------------+------------+-------------
| cust_id | cust_name | cust_source | cust_industry | cust_level |cust_linkman+---------+-----------+-------------+---------------+------------+-------------
| 15 | ttt | NULL | NULL | NULL | NULL
| 18 | ttt | NULL | NULL | NULL | NULL
+---------+-----------+-------------+---------------+------------+-------------
2 rows in set (0.00 sec)
1.测试事务是否生效
service层制造一个保存之后的异常,修改保存的代码:
访问请求:
http://localhost/SSHWeb/cus/saveCus.do?c.cust_name=ttt
结果:
日志
Hibernate:
insert
into
cst_customer
(cust_name, cust_source, cust_industry, cust_level, cust_linkman, cust_phone, cust_mobile)
values
(?, ?, ?, ?, ?, ?, ?)
2018-08-25 20:54:19 [cn.qlq.dao.CustomerDaoImpl]-[INFO] save customer success,userId is:19
2018-08-25 20:54:19 [org.springframework.orm.hibernate5.HibernateTransactionManager]-[DEBUG] Initiating transactionrollback
2018-08-25 20:54:19 [org.springframework.orm.hibernate5.HibernateTransactionManager]-[DEBUG] Rolling back Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[cn.qlq.domain.Customer#19]],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])]
2018-08-25 20:54:19 [org.springframework.orm.hibernate5.HibernateTransactionManager]-[DEBUG] Not closing pre-bound Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] after transaction
2018-08-25 20:54:19 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] trace com.mchange.v2.resourcepool.BasicResourcePool@7f29ce99 [managed: 5, unused: 3, excluded: 0] (e.g. com.mchange.v2.c3p0.impl.NewPooledConnection@383302c6)
2018-08-25 20:54:19 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
2018-08-25 20:54:19 [org.apache.struts2.json.JSONUtil]-[DEBUG] [JSON]{"msg":"保存客户失败!"}
分析:
插进去数据之后报异常然后进行 回滚操作,证明事务生效。
为了验证,我们在查询数据库:
mysql> select * fromcst_customer;+---------+-----------+-------------+---------------+------------+------------
| cust_id | cust_name | cust_source | cust_industry | cust_level |cust_linkma+---------+-----------+-------------+---------------+------------+------------
| 15 | ttt | NULL | NULL | NULL | NULL
| 18 | ttt | NULL | NULL | NULL | NULL
+---------+-----------+-------------+---------------+------------+------------
2 rows in set (0.00 sec)
2.在还存在异常的情况下,我们将事务注解去掉:
补充:
进行此测试,我们首先需要将重写OpenSessionInViewFilter过滤器的openSession方法,否则报错:Write operations are not allowed in read-only mode (FlushMode.MANUAL),
访问请求:
http://localhost/SSHWeb/cus/saveCus.do?c.cust_name=ttt
结果:
日志: (虽然报错了,但是错误是在执行save之后,所以会提交数据库并且没有事务控制不会回滚,造成异常数据)
Hibernate:
insert
into
cst_customer
(cust_name, cust_source, cust_industry, cust_level, cust_linkman, cust_phone, cust_mobile)
values
(?, ?, ?, ?, ?, ?, ?)2018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] acquire test -- pool size: 1; target_pool_size: 1; desired target? 2
2018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] incremented pending_acquires: 1
2018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] incremented pending_acquires: 2
2018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] incremented pending_acquires: 3
2018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] awaitAvailable(): com.mchange.v2.c3p0.impl.NewPooledConnection@379de7b42018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] trace com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91 [managed: 1, unused: 0, excluded: 0] (e.g. com.mchange.v2.c3p0.impl.NewPooledConnection@379de7b4)2018-08-25 21:38:52 [com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool]-[DEBUG] com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager@204746c3.acquireResource() returning.2018-08-25 21:38:52 [com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool]-[DEBUG] com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager@204746c3.acquireResource() returning.2018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] trace com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91 [managed: 2, unused: 1, excluded: 0] (e.g. com.mchange.v2.c3p0.impl.NewPooledConnection@379de7b4)2018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] decremented pending_acquires: 2
2018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] trace com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91 [managed: 3, unused: 2, excluded: 0] (e.g. com.mchange.v2.c3p0.impl.NewPooledConnection@379de7b4)2018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] decremented pending_acquires: 1
2018-08-25 21:38:52 [com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool]-[DEBUG] com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager@204746c3.acquireResource() returning.2018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] trace com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91 [managed: 4, unused: 3, excluded: 0] (e.g. com.mchange.v2.c3p0.impl.NewPooledConnection@379de7b4)2018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] decremented pending_acquires: 0
2018-08-25 21:38:52 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] trace com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91 [managed: 4, unused: 2, excluded: 0] (e.g. com.mchange.v2.c3p0.impl.NewPooledConnection@379de7b4)2018-08-25 21:38:52 [cn.qlq.dao.CustomerDaoImpl]-[INFO] save customer success,userId is:20
2018-08-25 21:38:55 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] Checking for expired resources - Sat Aug 25 21:38:55 CST 2018[com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91]2018-08-25 21:38:55 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] BEGIN check forexpired resources. [com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91]2018-08-25 21:38:55 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] FINISHED check forexpired resources. [com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91]2018-08-25 21:39:00 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] Checking for expired resources - Sat Aug 25 21:39:00 CST 2018[com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91]2018-08-25 21:39:00 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] BEGIN check forexpired resources. [com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91]2018-08-25 21:39:00 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] FINISHED check forexpired resources. [com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91]2018-08-25 21:39:05 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] Checking for expired resources - Sat Aug 25 21:39:05 CST 2018[com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91]2018-08-25 21:39:05 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] BEGIN check forexpired resources. [com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91]2018-08-25 21:39:05 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] FINISHED check forexpired resources. [com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91]2018-08-25 21:39:07 [cn.qlq.action.CustomerAction]-[ERROR]
java.lang.ArithmeticException:/by zero
at cn.qlq.service.CustomerServiceImpl.saveCustomer(CustomerServiceImpl.java:27)
at cn.qlq.action.CustomerAction.saveCus(CustomerAction.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
查看SQL:
mysql> select * fromcst_customer;+---------+-----------+-------------+---------------+------------+-------------
| cust_id | cust_name | cust_source | cust_industry | cust_level |cust_linkman+---------+-----------+-------------+---------------+------------+-------------
| 15 | ttt | NULL | NULL | NULL | NULL
| 18 | ttt | NULL | NULL | NULL | NULL
| 20 | ttt | NULL | NULL | NULL | NULL
+---------+-----------+-------------+---------------+------------+-------------
3 rows in set (0.00 sec)
总结:
证明我们上面的事务配置 是正确,并且也生效了。总结出来一条:在我们进行增(save)、改(update)、删(delete)的时候要在service层打上注解,否则会报Write operations are not allowed in read-only mode (FlushMode.MANUAL),除非我们重写OpenSessionInViewFilter的openSession方法(一般不推荐这种)。
5.测试DAO直接注入hibernateTemplate:
1.dao代码
packagecn.qlq.dao;importorg.hibernate.Session;importcn.qlq.domain.Customer;public interfaceCustomerDao2 {/*** 保存客户
*
*@paramc*/
public voidsaveCustomer(Customer c);/*** 根据ID查询
*
*@paramcusId
*@return
*/
publicCustomer getCustomerById(Long cusId);/*** 测试通过hibernate获取的session是否同一线程是一样的*/
publicSession testSessionIsSameInOneThread();
}
packagecn.qlq.dao;importjava.io.Serializable;importjava.util.List;importjavax.annotation.Resource;importorg.hibernate.Criteria;importorg.hibernate.FlushMode;importorg.hibernate.Query;importorg.hibernate.SQLQuery;importorg.hibernate.Session;importorg.hibernate.SessionFactory;importorg.hibernate.criterion.Restrictions;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.orm.hibernate5.HibernateTemplate;importorg.springframework.orm.hibernate5.support.HibernateDaoSupport;importorg.springframework.stereotype.Repository;importcn.qlq.domain.Customer;/***
* 测试Dao层直接注入hibernateTemplate
*
*@authorliqiang
**/@Repositorypublic class CustomerDaoImpl2 implementsCustomerDao2 {private static Logger log = LoggerFactory.getLogger(CustomerDaoImpl2.class);
@AutowiredprivateHibernateTemplate hibernateTemplate;
@Overridepublic voidsaveCustomer(Customer c) {
Serializable obj= hibernateTemplate.save(c);//返回值是生成的主键的值
log.info("save customer success,userId is:{}", obj.toString());
}
@OverridepublicCustomer getCustomerById(Long cusId) {//第一种:session.get方法
Session session =hibernateTemplate.getSessionFactory().openSession();
Customer customer= session.get(Customer.class, cusId);
log.info("第一种方法(session获取):{}", customer.toString());//第二种:HQL
String hql = "from Customer where cust_id=" +cusId;
Query query=session.createQuery(hql);
Customer cus=(Customer) query.uniqueResult();
log.info("第二种方法(HQL获取):{}", customer.toString());//第三种:Criteria查询
Criteria c = session.createCriteria(Customer.class);
c.add(Restrictions.eq("cust_name", "ttt"));
List list=c.list();
log.info("Criteria方法获取的:{}", list.toString());//第四种:原生SQL查询
String sql = "select * from cst_customer where cust_id = " +cusId;
SQLQuery sqlQuery=session.createSQLQuery(sql);
sqlQuery.addEntity(Customer.class);
log.info("原生SQL查询方法获取的:{}", sqlQuery.list().toString());returncustomer;
}
@OverridepublicSession testSessionIsSameInOneThread() {returnhibernateTemplate.getSessionFactory().getCurrentSession();
}
}
2.service代码:
3.Action层代码:
/*** 测试Dao层直接注入hibernateTemplate
*
*@return
*/@Action(value= "saveCus2", results ={
@Result(name= "success", type = "json", params = { "root", "responseMap"}) })publicString saveCus2() {try{
customerService.saveCustomer2(c);
}catch(Exception e) {
log.error("", e);
responseMap.put("msg", "保存客户失败!");return "success";
}
responseMap.put("msg", "保存客户成功!");return "success";
}
4.测试:
http://localhost/SSHWeb/cus/saveCus2.do?c.cust_name=ttt2
结果:
6.总结:
SSH整合与SSM整合大体类似,在这里就基本完成了SSH的整合。总结一下SSH整合过程。
1.Struts配置:配置struts的对象工程为spring,然后struts采用注解配置,struts整合spring比较简单
2.spring:配置扫描的包,引入配置文件。
3.spring整合hibernate:配置数据源(连接池)、
配置sessionFactory(注入数据源、hibernate配置文件位置(configLocations)、hibernate一些打印SQL等配置(hibernateProperties))
配置事务管理器并开启注解事务。
配置hibernateTemplate,需要注入会话工厂(sessionFactory)
接下来还有hibernate的二级缓存与注解的使用未完成,将会在之后补充。
7.Hibernate注解开发:
注解开发只是将原来的xml配置的信息用注解代替,项目使用过程中与原来一样。比如用注解配置上面客户和联系人的关系:
packagecn.qlq.domain;importjava.util.HashSet;importjava.util.Set;importjavax.persistence.CascadeType;importjavax.persistence.Entity;importjavax.persistence.FetchType;importjavax.persistence.GeneratedValue;importjavax.persistence.GenerationType;importjavax.persistence.Id;importjavax.persistence.JoinColumn;importjavax.persistence.OneToMany;importorg.hibernate.annotations.Index;
@Entitypublic classCustomer {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)privateLong cust_id;
@Index(name= "cust_nameIndex") //该注解来自Hibernate包
privateString cust_name;privateString cust_source;privateString cust_industry;privateString cust_level;privateString cust_linkman;privateString cust_phone;privateString cust_mobile;//使用set集合,表达一对多关系
@OneToMany(cascade = CascadeType.ALL, fetch =FetchType.LAZY)
@JoinColumn(name= "customer_id") //定义在linkman的客户的外键列名称
private Set linkMens = new HashSet();public SetgetLinkMens() {returnlinkMens;
}public void setLinkMens(SetlinkMens) {this.linkMens =linkMens;
}publicLong getCust_id() {returncust_id;
}public voidsetCust_id(Long cust_id) {this.cust_id =cust_id;
}publicString getCust_name() {returncust_name;
}public voidsetCust_name(String cust_name) {this.cust_name =cust_name;
}publicString getCust_source() {returncust_source;
}public voidsetCust_source(String cust_source) {this.cust_source =cust_source;
}publicString getCust_industry() {returncust_industry;
}public voidsetCust_industry(String cust_industry) {this.cust_industry =cust_industry;
}publicString getCust_level() {returncust_level;
}public voidsetCust_level(String cust_level) {this.cust_level =cust_level;
}publicString getCust_linkman() {returncust_linkman;
}public voidsetCust_linkman(String cust_linkman) {this.cust_linkman =cust_linkman;
}publicString getCust_phone() {returncust_phone;
}public voidsetCust_phone(String cust_phone) {this.cust_phone =cust_phone;
}publicString getCust_mobile() {returncust_mobile;
}public voidsetCust_mobile(String cust_mobile) {this.cust_mobile =cust_mobile;
}
@OverridepublicString toString() {return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + "]";
}publicCustomer(Long cust_id, String cust_name) {super();this.cust_id =cust_id;this.cust_name =cust_name;
}publicCustomer() {
}
}
packagecn.qlq.domain;importjavax.persistence.CascadeType;importjavax.persistence.Entity;importjavax.persistence.FetchType;importjavax.persistence.GeneratedValue;importjavax.persistence.GenerationType;importjavax.persistence.Id;importjavax.persistence.JoinColumn;importjavax.persistence.ManyToOne;//联系人实体
@Entitypublic classLinkMan {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)privateLong lkm_id;privateCharacter lkm_gender;privateString lkm_name;privateString lkm_phone;privateString lkm_email;privateString lkm_qq;privateString lkm_mobile;privateString lkm_memo;privateString lkm_position;//表达多对一关系
@ManyToOne(cascade = { CascadeType.ALL }, fetch =FetchType.EAGER)
@JoinColumn(name= "customer_id") //定义在linkman的客户的外键列名称
privateCustomer customer;publicCustomer getCustomer() {returncustomer;
}public voidsetCustomer(Customer customer) {this.customer =customer;
}publicLong getLkm_id() {returnlkm_id;
}public voidsetLkm_id(Long lkm_id) {this.lkm_id =lkm_id;
}publicCharacter getLkm_gender() {returnlkm_gender;
}public voidsetLkm_gender(Character lkm_gender) {this.lkm_gender =lkm_gender;
}publicString getLkm_name() {returnlkm_name;
}public voidsetLkm_name(String lkm_name) {this.lkm_name =lkm_name;
}publicString getLkm_phone() {returnlkm_phone;
}public voidsetLkm_phone(String lkm_phone) {this.lkm_phone =lkm_phone;
}publicString getLkm_email() {returnlkm_email;
}public voidsetLkm_email(String lkm_email) {this.lkm_email =lkm_email;
}publicString getLkm_qq() {returnlkm_qq;
}public voidsetLkm_qq(String lkm_qq) {this.lkm_qq =lkm_qq;
}publicString getLkm_mobile() {returnlkm_mobile;
}public voidsetLkm_mobile(String lkm_mobile) {this.lkm_mobile =lkm_mobile;
}publicString getLkm_memo() {returnlkm_memo;
}public voidsetLkm_memo(String lkm_memo) {this.lkm_memo =lkm_memo;
}publicString getLkm_position() {returnlkm_position;
}public voidsetLkm_position(String lkm_position) {this.lkm_position =lkm_position;
}
@OverridepublicString toString() {return "LinkMan [lkm_id=" + lkm_id + ", lkm_gender=" + lkm_gender + ", lkm_name=" + lkm_name + ", lkm_phone="
+ lkm_phone + ", lkm_email=" + lkm_email + ", lkm_qq=" + lkm_qq + ", lkm_mobile=" +lkm_mobile+ ", lkm_memo=" + lkm_memo + ", lkm_position=" + lkm_position + ", customer=" + customer + "]";
}
}
修改上面的hibernate.cfg.xml配置:
启动项目即可,查看hibernate自动建的表:(表结构与索引都已经添加好)
mysql>show tables;+---------------------+
| Tables_in_hibernate |
+---------------------+
| customer |
| hibernate_sequence |
| linkman |
+---------------------+
3 rows in set (0.00sec)
mysql> desccustomer;+---------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+-------+
| cust_id | bigint(20) | NO | PRI | NULL | |
| cust_industry | varchar(255) | YES | | NULL | |
| cust_level | varchar(255) | YES | | NULL | |
| cust_linkman | varchar(255) | YES | | NULL | |
| cust_mobile | varchar(255) | YES | | NULL | |
| cust_name | varchar(255) | YES | MUL | NULL | |
| cust_phone | varchar(255) | YES | | NULL | |
| cust_source | varchar(255) | YES | | NULL | |
+---------------+--------------+------+-----+---------+-------+
8 rows in set (0.01sec)
mysql>show tables;+---------------------+
| Tables_in_hibernate |
+---------------------+
| customer |
| hibernate_sequence |
| linkman |
+---------------------+
3 rows in set (0.00sec)
mysql> show create tablecustomer\G*************************** 1. row ***************************
Table: customerCreate Table: CREATE TABLE`customer` (
`cust_id`bigint(20) NOT NULL,
`cust_industry`varchar(255) DEFAULT NULL,
`cust_level`varchar(255) DEFAULT NULL,
`cust_linkman`varchar(255) DEFAULT NULL,
`cust_mobile`varchar(255) DEFAULT NULL,
`cust_name`varchar(255) DEFAULT NULL,
`cust_phone`varchar(255) DEFAULT NULL,
`cust_source`varchar(255) DEFAULT NULL,PRIMARY KEY(`cust_id`),KEY`cust_nameIndex` (`cust_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf81 row in set (0.00sec)
mysql> show create tablelinkman\G*************************** 1. row ***************************
Table: linkmanCreate Table: CREATE TABLE`linkman` (
`lkm_id`bigint(20) NOT NULL,
`lkm_email`varchar(255) DEFAULT NULL,
`lkm_gender`char(1) DEFAULT NULL,
`lkm_memo`varchar(255) DEFAULT NULL,
`lkm_mobile`varchar(255) DEFAULT NULL,
`lkm_name`varchar(255) DEFAULT NULL,
`lkm_phone`varchar(255) DEFAULT NULL,
`lkm_position`varchar(255) DEFAULT NULL,
`lkm_qq`varchar(255) DEFAULT NULL,
`customer_id`bigint(20) DEFAULT NULL,PRIMARY KEY(`lkm_id`),KEY`FKg1urlra7hn0a0hsr6c3nqxd86` (`customer_id`),CONSTRAINT `FKg1urlra7hn0a0hsr6c3nqxd86` FOREIGN KEY(`customer_id`) REFERENCE
) ENGINE=InnoDB DEFAULT CHARSET=utf81 row in set (0.00sec)
mysql> show create tablehibernate_sequence\G*************************** 1. row ***************************
Table: hibernate_sequenceCreate Table: CREATE TABLE`hibernate_sequence` (
`next_val`bigint(20) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf81 row in set (0.00 sec)
8.SSH整合encahe对Hibernate进行二级缓存
CTM的,一晚上SSH整合encache都有一个错,JAR包啥的都不缺,日狗了真是。。。。。
原来是因为maven项目中使用普通方式导入的包没生效,最后只能下载下来包然后添加到maven索引中才解决。。。。参考:https://www.cnblogs.com/qlqwjy/p/8385424.html
SSH中使用二级缓存类似于Hibernate项目单独使用ehcache,
添加ehcache、hibernate-ehcache的包,
session-factory开启二级缓存,
需要缓存的类实现Serializable接口,注解声明缓存的并发访问策略即可。
并不是所有的查询都会缓存,查询集合的时候由于集合没有实现Serializable接口,所以不会缓存,如果查询单个会缓存。根据多次请求发出的SQL请求数量即可判断是否缓存。
9.SSH整合Ehcache对service层进行缓存(注解缓存)
在这里整合的时候ehcache报错多个cache错误,所以将ehcache的版本降低到2.4.3.
spring中创建CacheManager,声明注解缓存开启:(引入spring-context-support的包)
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-3.1.xsd">
Service使用@Cacheable添加缓存,使用@CacheEvit清除缓存
packagecn.qlq.service;importjava.util.List;importorg.hibernate.Session;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.cache.annotation.CacheEvict;importorg.springframework.cache.annotation.Cacheable;importorg.springframework.orm.hibernate5.HibernateTemplate;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;importcn.qlq.dao.CustomerDao;importcn.qlq.dao.CustomerDao2;importcn.qlq.domain.Customer;importcn.qlq.utils.EhcacheUtils;importnet.sf.ehcache.Ehcache;
@Service
@Transactionalpublic class CustomerServiceImpl implementsCustomerService {private static final Logger log = LoggerFactory.getLogger(CustomerServiceImpl.class);
@AutowiredprivateCustomerDao customerDao;
@AutowiredprivateCustomerDao2 customerDao2;
@AutowiredprivateHibernateTemplate hibernateTemplate;/*** 删除缓存*/@CacheEvict(value= "cache_test", key = "'allCus'")
@Overridepublic booleansaveCustomer(Customer c) {
log.info("进入service方法----saveCustomer");
customerDao.saveCustomer(c);return true;
}
@Overridepublic booleansaveCustomer2(Customer c) {
customerDao2.saveCustomer(c);return true;
}@Cacheable(value= "cache_test", key = "'allCus'")
@Overridepublic ListlistAllCustomers() {
log.info("进入service方法----listAllCustomers");returncustomerDao2.listAllCustomers();
}
}
注意:@Cacheable和@CacheEvcit的value是ehcache缓存的name,需要在ehcache.xml中进行配置,key是存到cache的key,key如果是字符串需要加单引号,否则会按spel获取参数上的值会报错。
10 SSH整合ehcache手动添加缓存(代码中手动操作)
首先一个缓存的工具类:(如果设计的再好点可以设计为单例模式或者增加线程安全操作等优化)
packagecn.qlq.utils;importnet.sf.ehcache.Cache;importnet.sf.ehcache.CacheManager;importnet.sf.ehcache.Element;/*** ehcache 缓存工具类
*
* cacheName在ehcache.xml中配置*/
public classEhcacheUtils {public static final String HANDLE_CACHE = "handle_cache_test";public static CacheManager manager =CacheManager.create();public staticObject get(String cacheName, Object key) {
Cache cache=manager.getCache(cacheName);if (cache != null) {
Element element=cache.get(key);if (element != null) {returnelement.getObjectValue();
}
}return null;
}public static voidput(String cacheName, Object key, Object value) {
Cache cache=manager.getCache(cacheName);if (cache != null) {
cache.put(newElement(key, value));
}
}public static booleanremove(String cacheName, Object key) {
Cache cache=manager.getCache(cacheName);if (cache != null) {returncache.remove(key);
}return false;
}public static voidmain(String[] args) {
String key= "key";
String value= "hello";
EhcacheUtils.put("handle_cache_test", key, value);
System.out.println(EhcacheUtils.get("handle_cache_test", key));
}
}
模拟程序中Service层进行缓存:
/*** 查询完成的时候手动添加缓存*/@OverridepublicCustomer getCustomerById(Long cusId) {
Customer cus=(Customer) EhcacheUtils.get(EhcacheUtils.HANDLE_CACHE, cusId);if (cus == null) {
cus=customerDao.getCustomerById(cusId);
EhcacheUtils.put(EhcacheUtils.HANDLE_CACHE, cusId, cus);
log.info("添加缓存:{}", cus);returncus;
}else{
log.info("直接查的缓存的数据:{}", cus);returncus;
}
}
至此,SSH开发环境基本搭建完成,其中事务、缓存等也都搭建完成,开发中虽然更倾向于使用SSM+Redis缓存,但是还是研究了SSH开发的一整套流程,谁知道以后会不会用到,,,,