斷斷續續學習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 customer\G *************************** 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 linkman\G *************************** 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_sequence\G *************************** 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