断断续续学习hibernate也有一段时间了,在这里研究一下SSH开发环境的搭建过程,自己简单的搭建一个SSH的开发环境。采用maven搭建。
0.项目结构:
1.导包:(maven项目)
pom.xml配置文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.qlq</groupId> <artifactId>SSHWeb</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <!-- 声明变量,下面用类似于el表达式提取 --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.2.4.RELEASE</spring.version> <mysql.version>5.1.37</mysql.version> <aspectj.version>1.6.10</aspectj.version> </properties> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <!-- spring 依赖包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <!-- myql 依赖包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.0.7.Final</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-convention-plugin</artifactId> <version>2.3.24</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.3.24</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-json-plugin</artifactId> <version>2.3.24</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> <version>2.3.24</version> </dependency> <!-- slf4j 依赖包 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.5</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.0-rc1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.0-rc1</version> </dependency> </dependencies> <build> <!-- 配置了很多插件 --> <plugins> <!-- 编译插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>1.7</source> <target>1.7</target> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- tomcat7插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>80</port> <path>/ssh</path> <uriEncoding>UTF-8</uriEncoding> <server>tomcat7</server> </configuration> </plugin> </plugins> </build> </project>
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
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" 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 "> <!-- 引入属性文件 --> <context:property-placeholder location="classpath:db.properties" /> <!-- 自动扫描dao和service包(自动注入) --> <context:component-scan base-package="cn.qlq" /> </beans>
spring-hibernate.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" 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 "> <!-- 使用C3P0数据源,MySQL数据库 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <!-- MySQL5 --> <property name="driverClass" value="${driverClassName}"></property> <property name="jdbcUrl" value="${url}"></property> <property name="user" value="${username}"></property> <property name="password" value="${password}"></property> <property name="maxPoolSize" value="40"></property> <property name="minPoolSize" value="1"></property> <property name="initialPoolSize" value="1"></property> <property name="maxIdleTime" value="20"></property> </bean> <!-- session工厂 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocations" value="classpath:hibernate.cfg.xml"></property> <property name="packagesToScan" value="cn.qlq.domain" /> <property name="hibernateProperties"> <props> <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <prop key="hibernate.format_sql">${hibernate.format_sql}</prop> </props> </property> </bean> <!-- 配置事务 --> <bean name="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <tx:annotation-driven transaction-manager="txManager" /> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> </beans>
2.struts.xml
配置一些常量,采用注解开发。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.i18n.encoding" value="utf-8"></constant> <constant name="devMode" value="true"></constant> <constant name="struts.enable.DynamicMethodInvocation" value="true" /> <constant name="" value="true" /> <!-- 配置拦截的后缀 --> <constant name="struts.action.extension" value="action,do" /> <!-- 与spring整合 --> <constant name="struts.objectFactory" value="spring"></constant> <package name="default" extends="json-default"></package> </struts>
3.hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <mapping resource="cn/qlq/domain/Customer.hbm.xml" /> <mapping resource="cn/qlq/domain/LinkMan.hbm.xml" /> </session-factory> </hibernate-configuration>
4.web.xml
spring监听器、配置文件路径、hibernate会话过滤器(一次请求前开启session,请求完成关闭session)、struts入口过滤器(注意hibernate打开会话过滤器必须配置在struts入口前面)
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>ssh</display-name> <!-- Spring ApplicationContext配置文件的路径,可使用通配符,多个路径用,号分隔 此参数用于后面的Spring Context Loader --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring.xml,classpath:spring-hibernate.xml</param-value> </context-param> <!-- spring 监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Filter 定义 --> <!-- Character Encoding filter --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--Hibernate的session丢失解决方法 --> <filter> <filter-name>openSessionInView</filter-name> <filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>openSessionInView</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- struts2 filter --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
3.接下来就是测试代码:
0.实体类以及xml配置文件
Customer.java
package cn.qlq.domain; import java.util.HashSet; import java.util.Set; public class Customer { private Long cust_id; private String cust_name; private String cust_source; private String cust_industry; private String cust_level; private String cust_linkman; private String cust_phone; private String cust_mobile; //使用set集合,表达一对多关系 private Set<LinkMan> linkMens = new HashSet<LinkMan>(); public Set<LinkMan> getLinkMens() { return linkMens; } public void setLinkMens(Set<LinkMan> linkMens) { this.linkMens = linkMens; } public Long getCust_id() { return cust_id; } public void setCust_id(Long cust_id) { this.cust_id = cust_id; } public String getCust_name() { return cust_name; } public void setCust_name(String cust_name) { this.cust_name = cust_name; } public String getCust_source() { return cust_source; } public void setCust_source(String cust_source) { this.cust_source = cust_source; } public String getCust_industry() { return cust_industry; } public void setCust_industry(String cust_industry) { this.cust_industry = cust_industry; } public String getCust_level() { return cust_level; } public void setCust_level(String cust_level) { this.cust_level = cust_level; } public String getCust_linkman() { return cust_linkman; } public void setCust_linkman(String cust_linkman) { this.cust_linkman = cust_linkman; } public String getCust_phone() { return cust_phone; } public void setCust_phone(String cust_phone) { this.cust_phone = cust_phone; } public String getCust_mobile() { return cust_mobile; } public void setCust_mobile(String cust_mobile) { this.cust_mobile = cust_mobile; } @Override public String toString() { return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + "]"; } public Customer(Long cust_id, String cust_name) { super(); this.cust_id = cust_id; this.cust_name = cust_name; } public Customer() { } }
Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.qlq.domain" > <class name="Customer" table="cst_customer"> <id name="cust_id" > <generator class="native"></generator> </id> <property name="cust_name" column="cust_name" > </property> <property name="cust_source" column="cust_source" ></property> <property name="cust_industry" column="cust_industry" ></property> <property name="cust_level" column="cust_level" ></property> <property name="cust_linkman" column="cust_linkman" ></property> <property name="cust_phone" column="cust_phone" ></property> <property name="cust_mobile" column="cust_mobile" ></property> <!-- lazy属性: 决定是否延迟加载 true(默认值): 延迟加载,懒加载 false: 立即加载 extra: 极其懒惰 fetch属性: 决定加载策略.使用什么类型的sql语句加载集合数据 select(默认值): 单表查询加载 join: 使用多表查询加载集合 subselect:使用子查询加载集合 --> <!-- batch-size: 5 抓取集合的数量为5. 抓取客户的集合时,一次抓取几个客户的联系人集合. --> <set name="linkMens" batch-size="5"> <key column="lkm_cust_id" ></key> <one-to-many class="LinkMan" /> </set> </class> </hibernate-mapping>
LinkMan.java
package cn.qlq.domain; //联系人实体 public class LinkMan { private Long lkm_id; private Character lkm_gender; private String lkm_name; private String lkm_phone; private String lkm_email; private String lkm_qq; private String lkm_mobile; private String lkm_memo; private String lkm_position; // 表达多对一关系 private Customer customer; public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public Long getLkm_id() { return lkm_id; } public void setLkm_id(Long lkm_id) { this.lkm_id = lkm_id; } public Character getLkm_gender() { return lkm_gender; } public void setLkm_gender(Character lkm_gender) { this.lkm_gender = lkm_gender; } public String getLkm_name() { return lkm_name; } public void setLkm_name(String lkm_name) { this.lkm_name = lkm_name; } public String getLkm_phone() { return lkm_phone; } public void setLkm_phone(String lkm_phone) { this.lkm_phone = lkm_phone; } public String getLkm_email() { return lkm_email; } public void setLkm_email(String lkm_email) { this.lkm_email = lkm_email; } public String getLkm_qq() { return lkm_qq; } public void setLkm_qq(String lkm_qq) { this.lkm_qq = lkm_qq; } public String getLkm_mobile() { return lkm_mobile; } public void setLkm_mobile(String lkm_mobile) { this.lkm_mobile = lkm_mobile; } public String getLkm_memo() { return lkm_memo; } public void setLkm_memo(String lkm_memo) { this.lkm_memo = lkm_memo; } public String getLkm_position() { return lkm_position; } public void setLkm_position(String lkm_position) { this.lkm_position = lkm_position; } @Override public String 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
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.qlq.domain" > <class name="LinkMan" table="cst_linkman" > <id name="lkm_id" > <generator class="native"></generator> </id> <property name="lkm_gender" ></property> <property name="lkm_name" ></property> <property name="lkm_phone" ></property> <property name="lkm_email" ></property> <property name="lkm_qq" ></property> <property name="lkm_mobile" ></property> <property name="lkm_memo" ></property> <property name="lkm_position" ></property> <!-- fetch 决定加载的sql语句 select: 使用单表查询 join : 多表查询 (会导致lazy失效) lazy 决定加载时机 false: 立即加载 proxy: 由customer的类级别加载策略决定. --> <many-to-one name="customer" column="lkm_cust_id" class="Customer" fetch="join" lazy="proxy"></many-to-one> </class> </hibernate-mapping>
1.dao接口以及实现
package cn.qlq.dao; import org.hibernate.Session; import cn.qlq.domain.Customer; public interface CustomerDao { /** * 保存客户 * * @param c */ public void saveCustomer(Customer c); /** * 根据ID查询 * * @param cusId * @return */ public Customer getCustomerById(Long cusId); /** * 测试通过hibernate获取的session是否同一线程是一样的 */ public Session testSessionIsSameInOneThread(); }
package cn.qlq.dao; import java.io.Serializable; import java.util.List; import javax.annotation.Resource; import org.hibernate.Criteria; import org.hibernate.Query; import org.hibernate.SQLQuery; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Restrictions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.orm.hibernate5.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import cn.qlq.domain.Customer; /** * dao操作一般继承HibernateDaoSupport,里面获取hibernateTemplete也可以进行许多操作 * * @author liqiang * */ @Repository public class CustomerDaoImpl extends HibernateDaoSupport implements CustomerDao { private static Logger log = LoggerFactory.getLogger(CustomerDaoImpl.class); @Override public void saveCustomer(Customer c) { Serializable obj = getHibernateTemplate().save(c);// 返回值是生成的主键的值 log.info("save customer success,userId is:{}", obj.toString()); } @Resource public void setSessionFacotry(SessionFactory sessionFacotry) { super.setSessionFactory(sessionFacotry); } @Override public Customer 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()); return customer; } @Override public Session testSessionIsSameInOneThread() { return getHibernateTemplate().getSessionFactory().getCurrentSession(); } }
dao实现一般要继承HibernateDaoSupport,我们项目中也是采用所有的Dao都继承HibernateDaoSupport。这个抽象类有好多直接可以用的方法。原理都是调用hibernateTemplate。所以在Dao实现层我们可以选择继承HibernateDaoSupport,或者直接注入HibernateTemplate。
也可以用HibernateTemplate进行操作(直接注入HibernateTemplate),此方法的实例我们已经在XML中注入,通过此对象可以获取session执行原生SQL,或者直接注入SessionFactory获取Session(不建议这种)。
2.service接口和实现
package cn.qlq.service; import cn.qlq.domain.Customer; public interface CustomerService { public boolean saveCustomer(Customer c); /** * 根据ID查询 * * @param cusId * @return */ public Customer getCustomerById(Long cusId); /** * 测试通过hibernateTemplete获取的session是否同一线程是一样的 */ public boolean testSessionIsSameInOneThread(); }
package cn.qlq.service; import org.hibernate.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate5.HibernateTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import cn.qlq.dao.CustomerDao; import cn.qlq.domain.Customer; @Service @Transactional public class CustomerServiceImpl implements CustomerService { private static Logger log = LoggerFactory.getLogger(CustomerServiceImpl.class); @Autowired private CustomerDao customerDao; @Autowired private HibernateTemplate hibernateTemplate; @Override public boolean saveCustomer(Customer c) { customerDao.saveCustomer(c); return true; } @Override public Customer getCustomerById(Long cusId) { return customerDao.getCustomerById(cusId); } @Override public boolean testSessionIsSameInOneThread() { //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)); return daoSession.equals(serviceSession); } }
3.Action层代码
package cn.qlq.action; import java.util.HashMap; import java.util.Map; import org.apache.struts2.convention.annotation.Action; import org.apache.struts2.convention.annotation.Namespace; import org.apache.struts2.convention.annotation.ParentPackage; import org.apache.struts2.convention.annotation.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import cn.qlq.domain.Customer; import cn.qlq.service.CustomerService; /** * 客户Action * * @author liqiang * */ @Namespace("/cus") @ParentPackage("default") @SuppressWarnings("all") public class CustomerAction { private static Logger log = LoggerFactory.getLogger(CustomerAction.class); @Autowired private CustomerService customerService; private Map responseMap = new HashMap(); private Customer c;// 对象驱动保存对象 @Action(value = "saveCus", results = { @Result(name = "success", type = "json", params = { "root", "responseMap" }) }) public String 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" }) }) public String 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" }) }) public String testSessionIsSameInOneThread() { responseMap.put("data", customerService.testSessionIsSameInOneThread()); return "success"; } public Customer getC() { return c; } public void setC(Customer c) { this.c = c; } public Map getResponseMap() { return responseMap; } public void setResponseMap(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 * from cst_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 transaction rollback
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 * from cst_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),
参考:https://www.cnblogs.com/qlqwjy/p/9535500.html
访问请求:
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@379de7b4 2018-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 for expired resources. [com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91] 2018-08-25 21:38:55 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] FINISHED check for expired 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 for expired resources. [com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91] 2018-08-25 21:39:00 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] FINISHED check for expired 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 for expired resources. [com.mchange.v2.resourcepool.BasicResourcePool@4c3a3f91] 2018-08-25 21:39:05 [com.mchange.v2.resourcepool.BasicResourcePool]-[DEBUG] FINISHED check for expired 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 * from cst_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代码
package cn.qlq.dao; import org.hibernate.Session; import cn.qlq.domain.Customer; public interface CustomerDao2 { /** * 保存客户 * * @param c */ public void saveCustomer(Customer c); /** * 根据ID查询 * * @param cusId * @return */ public Customer getCustomerById(Long cusId); /** * 测试通过hibernate获取的session是否同一线程是一样的 */ public Session testSessionIsSameInOneThread(); }
package cn.qlq.dao; import java.io.Serializable; import java.util.List; import javax.annotation.Resource; import org.hibernate.Criteria; import org.hibernate.FlushMode; import org.hibernate.Query; import org.hibernate.SQLQuery; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Restrictions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate5.HibernateTemplate; import org.springframework.orm.hibernate5.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import cn.qlq.domain.Customer; /** * * 测试Dao层直接注入hibernateTemplate * * @author liqiang * */ @Repository public class CustomerDaoImpl2 implements CustomerDao2 { private static Logger log = LoggerFactory.getLogger(CustomerDaoImpl2.class); @Autowired private HibernateTemplate hibernateTemplate; @Override public void saveCustomer(Customer c) { Serializable obj = hibernateTemplate.save(c);// 返回值是生成的主键的值 log.info("save customer success,userId is:{}", obj.toString()); } @Override public Customer 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()); return customer; } @Override public Session testSessionIsSameInOneThread() { return hibernateTemplate.getSessionFactory().getCurrentSession(); } }
2.service代码:
3.Action层代码:
/** * 测试Dao层直接注入hibernateTemplate * * @return */ @Action(value = "saveCus2", results = { @Result(name = "success", type = "json", params = { "root", "responseMap" }) }) public String 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注解开发:
hibernate注解的使用参考我的另一篇博客:https://www.cnblogs.com/qlqwjy/p/9545453.html
注解开发只是将原来的xml配置的信息用注解代替,项目使用过程中与原来一样。比如用注解配置上面客户和联系人的关系:
package cn.qlq.domain; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import org.hibernate.annotations.Index; @Entity public class Customer { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long cust_id; @Index(name = "cust_nameIndex") // 该注解来自Hibernate包 private String cust_name; private String cust_source; private String cust_industry; private String cust_level; private String cust_linkman; private String cust_phone; private String cust_mobile; // 使用set集合,表达一对多关系 @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "customer_id") // 定义在linkman的客户的外键列名称 private Set<LinkMan> linkMens = new HashSet<LinkMan>(); public Set<LinkMan> getLinkMens() { return linkMens; } public void setLinkMens(Set<LinkMan> linkMens) { this.linkMens = linkMens; } public Long getCust_id() { return cust_id; } public void setCust_id(Long cust_id) { this.cust_id = cust_id; } public String getCust_name() { return cust_name; } public void setCust_name(String cust_name) { this.cust_name = cust_name; } public String getCust_source() { return cust_source; } public void setCust_source(String cust_source) { this.cust_source = cust_source; } public String getCust_industry() { return cust_industry; } public void setCust_industry(String cust_industry) { this.cust_industry = cust_industry; } public String getCust_level() { return cust_level; } public void setCust_level(String cust_level) { this.cust_level = cust_level; } public String getCust_linkman() { return cust_linkman; } public void setCust_linkman(String cust_linkman) { this.cust_linkman = cust_linkman; } public String getCust_phone() { return cust_phone; } public void setCust_phone(String cust_phone) { this.cust_phone = cust_phone; } public String getCust_mobile() { return cust_mobile; } public void setCust_mobile(String cust_mobile) { this.cust_mobile = cust_mobile; } @Override public String toString() { return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + "]"; } public Customer(Long cust_id, String cust_name) { super(); this.cust_id = cust_id; this.cust_name = cust_name; } public Customer() { } }
package cn.qlq.domain; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; //联系人实体 @Entity public class LinkMan { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long lkm_id; private Character lkm_gender; private String lkm_name; private String lkm_phone; private String lkm_email; private String lkm_qq; private String lkm_mobile; private String lkm_memo; private String lkm_position; // 表达多对一关系 @ManyToOne(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) @JoinColumn(name = "customer_id") // 定义在linkman的客户的外键列名称 private Customer customer; public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public Long getLkm_id() { return lkm_id; } public void setLkm_id(Long lkm_id) { this.lkm_id = lkm_id; } public Character getLkm_gender() { return lkm_gender; } public void setLkm_gender(Character lkm_gender) { this.lkm_gender = lkm_gender; } public String getLkm_name() { return lkm_name; } public void setLkm_name(String lkm_name) { this.lkm_name = lkm_name; } public String getLkm_phone() { return lkm_phone; } public void setLkm_phone(String lkm_phone) { this.lkm_phone = lkm_phone; } public String getLkm_email() { return lkm_email; } public void setLkm_email(String lkm_email) { this.lkm_email = lkm_email; } public String getLkm_qq() { return lkm_qq; } public void setLkm_qq(String lkm_qq) { this.lkm_qq = lkm_qq; } public String getLkm_mobile() { return lkm_mobile; } public void setLkm_mobile(String lkm_mobile) { this.lkm_mobile = lkm_mobile; } public String getLkm_memo() { return lkm_memo; } public void setLkm_memo(String lkm_memo) { this.lkm_memo = lkm_memo; } public String getLkm_position() { return lkm_position; } public void setLkm_position(String lkm_position) { this.lkm_position = lkm_position; } @Override public String 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配置:
<session-factory> <mapping class="cn.qlq.domain.Customer" /> <mapping class="cn.qlq.domain.LinkMan" /> </session-factory>
启动项目即可,查看hibernate自动建的表:(表结构与索引都已经添加好)
mysql> show tables; +---------------------+ | Tables_in_hibernate | +---------------------+ | customer | | hibernate_sequence | | linkman | +---------------------+ 3 rows in set (0.00 sec) mysql> desc customer; +---------------+--------------+------+-----+---------+-------+ | 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.01 sec) mysql> show tables; +---------------------+ | Tables_in_hibernate | +---------------------+ | customer | | hibernate_sequence | | linkman | +---------------------+ 3 rows in set (0.00 sec) mysql> show create table customerG *************************** 1. row *************************** Table: customer Create 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=utf8 1 row in set (0.00 sec) mysql> show create table linkmanG *************************** 1. row *************************** Table: linkman Create 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=utf8 1 row in set (0.00 sec) mysql> show create table hibernate_sequenceG *************************** 1. row *************************** Table: hibernate_sequence Create Table: CREATE TABLE `hibernate_sequence` ( `next_val` bigint(20) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec)
8.SSH整合encahe对Hibernate进行二级缓存
hibernate整合encache进行二级缓存参考:https://www.cnblogs.com/qlqwjy/p/9539120.html
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层进行缓存(注解缓存)
这个功能非常重要,将不需要Hibernate的二级缓存,关于spring注解缓存参考:https://www.cnblogs.com/qlqwjy/p/8574121.html
在这里整合的时候ehcache报错多个cache错误,所以将ehcache的版本降低到2.4.3.
- spring中创建CacheManager,声明注解缓存开启:(引入spring-context-support的包)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" 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"> <!-- 引入属性文件 --> <context:property-placeholder location="classpath:db.properties" /> <!-- 自动扫描dao和service包(自动注入) --> <context:component-scan base-package="cn.qlq" /> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> <property name="cacheManager" ref="ehcache"></property> </bean> <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation" value="classpath:ehcache.xml"></property> </bean> <cache:annotation-driven cache-manager="cacheManager" /> </beans>
- Service使用@Cacheable添加缓存,使用@CacheEvit清除缓存
package cn.qlq.service; import java.util.List; import org.hibernate.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.orm.hibernate5.HibernateTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import cn.qlq.dao.CustomerDao; import cn.qlq.dao.CustomerDao2; import cn.qlq.domain.Customer; import cn.qlq.utils.EhcacheUtils; import net.sf.ehcache.Ehcache; @Service @Transactional public class CustomerServiceImpl implements CustomerService { private static final Logger log = LoggerFactory.getLogger(CustomerServiceImpl.class); @Autowired private CustomerDao customerDao; @Autowired private CustomerDao2 customerDao2; @Autowired private HibernateTemplate hibernateTemplate; /** * 删除缓存 */ @CacheEvict(value = "cache_test", key = "'allCus'") @Override public boolean saveCustomer(Customer c) { log.info("进入service方法----saveCustomer"); customerDao.saveCustomer(c); return true; } @Override public boolean saveCustomer2(Customer c) { customerDao2.saveCustomer(c); return true; } @Cacheable(value = "cache_test", key = "'allCus'") @Override public List<Customer> listAllCustomers() { log.info("进入service方法----listAllCustomers"); return customerDao2.listAllCustomers(); } }
注意:@Cacheable和@CacheEvcit的value是ehcache缓存的name,需要在ehcache.xml中进行配置,key是存到cache的key,key如果是字符串需要加单引号,否则会按spel获取参数上的值会报错。
10 SSH整合ehcache手动添加缓存(代码中手动操作)
首先一个缓存的工具类:(如果设计的再好点可以设计为单例模式或者增加线程安全操作等优化)
package cn.qlq.utils; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; /** * ehcache 缓存工具类 * * cacheName在ehcache.xml中配置 */ public class EhcacheUtils { public static final String HANDLE_CACHE = "handle_cache_test"; public static CacheManager manager = CacheManager.create(); public static Object get(String cacheName, Object key) { Cache cache = manager.getCache(cacheName); if (cache != null) { Element element = cache.get(key); if (element != null) { return element.getObjectValue(); } } return null; } public static void put(String cacheName, Object key, Object value) { Cache cache = manager.getCache(cacheName); if (cache != null) { cache.put(new Element(key, value)); } } public static boolean remove(String cacheName, Object key) { Cache cache = manager.getCache(cacheName); if (cache != null) { return cache.remove(key); } return false; } public static void main(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层进行缓存:
/** * 查询完成的时候手动添加缓存 */ @Override public Customer 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); return cus; } else { log.info("直接查的缓存的数据:{}", cus); return cus; } }
至此,SSH开发环境基本搭建完成,其中事务、缓存等也都搭建完成,开发中虽然更倾向于使用SSM+Redis缓存,但是还是研究了SSH开发的一整套流程,谁知道以后会不会用到,,,,
git地址:https://github.com/qiao-zhi/SSHWeb.git