1:Spring整合Hibernate
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE hibernate-configuration PUBLIC 3 "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 4 "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> 5 <hibernate-configuration> 6 <session-factory> 7 <!-- 配置 hibernate 的基本属性 --> 8 9 <!-- 方言 --> 10 <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> 11 12 <!-- 是否显示及格式化 SQL --> 13 <property name="hibernate.show_sql">true</property> 14 <property name="hibernate.format_sql">true</property> 15 16 <!-- 生成数据表的策略 --> 17 <property name="hibernate.hbm2ddl.auto">update</property> 18 19 <!-- 二级缓存相关 --> 20 21 </session-factory> 22 23 </hibernate-configuration>
jdbc.user=luwei jdbc.password=luwei jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbcUrl=jdbc:mysql:///ssh jdbc.initPoolSize=5 jdbc.maxPoolSize=10 #...
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"> <!-- 导入资源文件 --> <context:property-placeholder location="classpath:db.properties"/> <!-- 配置 C3P0 数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property> <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property> </bean> <!-- 配置 SessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> <property name="mappingLocations" value="classpath:com/ssh/entities/*.hbm.xml"></property> </bean> </beans>
2:整合struts2
<?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.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="default" namespace="/" extends="struts-default"> <action name="emp_*" class="employeeAction" method="{1}"> <result name="list">/WEB-INF/views/emp-list.jsp</result> </action> </package> </struts>
<bean id="employeeAction" class="com.ssh.action.EmployeeAction" scope="prototype">
<property name="employeeService" ref="employeeService"></property>
</bean> <bean id="employeeService" class="com.ssh.service.EmployeeService"> <property name="employeeDao" ref="employeeDao"></property> </bean> <bean id="employeeDao" class="com.ssh.dao.EmployeeDao" scope="prototype"> <property name="sessionFactory" ref="sessionFactory"/> </bean>
3:开始写应用:
1):两个实体类:部门和员工
package com.ssh.entities; public class Department { private Integer id; private String departmentName; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getDepartmentName() { return departmentName; } public void setDepartmentName(String departmentName) { this.departmentName = departmentName; } @Override public String toString() { return "Department [id=" + id + ", departmentName=" + departmentName + "]"; } }
package com.ssh.entities; import java.util.Date; public class Employee { private Integer id; // 不能被修改 private String lastName; private String email; // 从前端传入的是 String 类型, 所以需要注意转换 private Date birth; // 不能被修改 private Date createTime; // 单向 n-1 的关联关系 private Department department; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Department getDepartment() { return department; } public void setDepartment(Department department) { this.department = department; } @Override public String toString() { return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", birth=" + birth + ", createTime=" + createTime + ", department=" + department + "]"; } }
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2014-7-22 11:21:48 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping> <class name="com.ssh.entities.Department" table="SSH_DEPARTMENT"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="native" /> </id> <property name="departmentName" type="java.lang.String"> <column name="DEPARTMENT_NAME" /> </property> </class> </hibernate-mapping>
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2014-7-22 11:21:48 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping> <class name="com.ssh.entities.Employee" table="SSH_EMPLOYEE"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="native" /> </id> <property name="lastName" type="java.lang.String"> <column name="LAST_NAME" /> </property> <property name="email" type="java.lang.String"> <column name="EMAIL" /> </property> <property name="birth" type="java.util.Date"> <column name="BIRTH" /> </property> <property name="createTime" type="java.util.Date"> <column name="CREATE_TIME" /> </property> <!-- 映射单向 n-1 的关联关系 --> <many-to-one name="department" class="com.ssh.entities.Department"> <column name="DEPARTMENT_ID" /> </many-to-one> </class> </hibernate-mapping>
2):写DAO层。这个层面就是为了从数据库中查询出数据
package com.ssh.dao; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; import com.ssh.entities.Employee; public class EmployeeDao { private SessionFactory sessionFactory; public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public Session getSession(){ return this.sessionFactory.getCurrentSession(); } public List<Employee> getAll(){ String hql = "FROM Employee e LEFT OUTER JOIN FETCH e.department"; return getSession().createQuery(hql).list(); } }
得到的值放在getAll()方法中。
3):写service层
package com.ssh.service; import java.util.List; import com.ssh.dao.EmployeeDao; import com.ssh.entities.Employee; public class EmployeeService { private EmployeeDao employeeDao; public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; } //现在从数据库中得到employee所有的数据。得到的employee信息放到request.在Action中写。 public List<Employee> getAll(){ List<Employee> employees=employeeDao.getAll(); return employees; } }
4:):写Action层
package com.ssh.action; import java.util.Map; import org.apache.struts2.interceptor.RequestAware; import com.opensymphony.xwork2.ActionSupport; import com.ssh.service.EmployeeService; public class EmployeeAction extends ActionSupport implements RequestAware{ /** * */ private static final long serialVersionUID = 1L; private EmployeeService employeeService; public void setEmployeeService(EmployeeService employeeService) { this.employeeService = employeeService; }
//把在service中得到数据存放到request作用域中。然后在页面上直接可以这个request作用域就得到相应的数据。 private Map<String, Object> request; public void setRequest(Map<String, Object> arg0) { this.request = arg0; } public String list() { System.out.println("1"); request.put("employees", employeeService.getAll()); System.out.println("2"); return "list"; } }
//主页面
<a href="emp_list">List All Employees</a>
展示的页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>显示所有的信息</title> </head> <body> <s:if test="#request.employees ==null || #request.employees.size()==0"> 没有任何 信息 </s:if> <s:else> <table border="1" cellpadding="10" cellspacing="0"> <tr> <td>ID</td> <td>LASTNAME</td> <td>EMAIL</td> <td>BIRTH</td> <td>CREATETIME</td> <td>DEPT</td> </tr> <s:iterator value="#request.employees"> <tr> <td>${id }</td> <td>${lastName }</td> <td>${email }</td> <td>${birth }</td> <td>${createTime }</td> <td>${department.departmentName }</td> </tr> </s:iterator> </table> </s:else> </body> </html>
这里会出现一个no session 懒加载异常。是因为在查询department的时候没有立即加载。用的时候才加载。但是session已经被hibernate给关闭了。
解决:使用迫切左外连接。先把数据查询出来。
FROM Employee e LEFT OUTER JOIN FETCH e.department
理解:1:action,service,dao都是类。然后让spring去管理这些类。加载类。bean。然后利用set注入的方式。把值给传入到类中。
2:写Action的时候一定要加上 scope="prototype"
3:创建request的时候继承RequestAware接口。
4:简单删除:
1):页面上的获取id
1 <td><a href="emp_delete?id=${id }">删除</a></td>
2):跳转
<result name="success" type="redirect">/emp_list</result>
3):从数据库中删除这条id的信息
public void delete(Integer id){ String hql="DELETE FROM Employee e WHERE e.id=?"; getSession().createQuery(hql).setInteger(0, id).executeUpdate(); //setInteger(0,id) 说明是第一个参数。executeUpdate() 执行修改。 }
4):service获取删除这条数据的信息
public void delete(Integer id){ employeeDao.delete(id); }
5):Action得到
private Integer id; public void setId(Integer id) { this.id = id; } public String delete(){ employeeService.delete(id); return "success"; }
理解:从页面中得到id。然后在action中设置一个成员变量。setId方法中。形参赋值给成员变量id。然后this.id得到页面上的值,传递给service里delete方法的形参。然后传递给
dao中的delete方法的形参。然后hibernate从数据库中删除数据。
5:AJAX删除:
1 <td><a href="emp_delete?id=${id }" class="delete">删除</a> 2 <input type="hidden" value="${lastName }"> 3 </td>
给a节点加上class类。function直接用class的名字。然后再弄一个隐藏域,用来显示名字。
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script> <script type="text/javascript"> $(function(){ //用类的名字 $(".delete").click(function(){ //$(this) 当前节点a .next(":hidden") 下一个节点的隐藏域。.val() 的值 var lastName = $(this).next(":hidden").val(); var flag = confirm("确定要删除" + lastName + "的信息吗?"); if(flag){ //删除后这个td也要删除掉。a节点的父节点td的父节点tr。 var $tr=$(this).parent().parent(); var url=this.href; var args={"time":new Date()}; $.post(url,args,function(data){ if(data =="1"){ alert("删除成功"); $tr.remove(); }else{ alert("删除失败"); } }); } //先把超链接的删除给取消掉 return false; }); })
if(data =="1"){
alert("删除成功");
$tr.remove();
}else{
alert("删除失败"); } 这句话要用到ajax在struts2中如何使用。
用inputStream
private InputStream inputStream; public InputStream getInputStream() { return inputStream; } public String delete(){ try { employeeService.delete(id2); inputStream = new ByteArrayInputStream("1".getBytes("UTF-8")); } catch (Exception e) { e.printStackTrace(); try { inputStream = new ByteArrayInputStream("0".getBytes("UTF-8")); } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } } return "delete"; }
注意这个返回值 return "delete";
在struts.xml中的跳转。不用去重定向到emp_list了。name就是返回值。
<result type="stream" name="delete"> <param name="contentType">text/html</param> <param name="inputName">inputStream</param> </result>
6:添加一个员工信息
需要先把是哪个部门给查询出来
因为每个Dao都要用到Session。所以写一个类BaseDao
1 package com.ssh.dao; 2 3 import org.hibernate.Session; 4 import org.hibernate.SessionFactory; 5 6 public class BaseDao { 7 private SessionFactory sessionFactory; 8 9 public void setSessionFactory(SessionFactory sessionFactory) { 10 this.sessionFactory = sessionFactory; 11 } 12 public Session getSession(){ 13 return this.sessionFactory.getCurrentSession(); 14 } 15 16 }
从数据库中查询出部门
public class DepartmentDao extends BaseDao{ public List<Department> getAll(){ String hql ="FROM Department"; return getSession().createQuery(hql).list(); } }
Service
public class DepartmentService { private DepartmentDao departmentDao; public void setDepartmentDao(DepartmentDao departmentDao) { this.departmentDao = departmentDao; } public List<Department> getAll(){ return departmentDao.getAll(); } }
在Action中写个input方法,用来把Department的方法存储到request作用域中
public String input(){ request.put("departments", departmentService.getAll()); return INPUT; }
用Spring来管理Service和Dao
<bean id="employeeAction" class="com.ssh.action.EmployeeAction" scope="prototype"> <property name="employeeService" ref="employeeService"></property> <property name="departmentService" ref="departmentService"></property> </bean> <bean id="employeeService" class="com.ssh.service.EmployeeService"> <property name="employeeDao" ref="employeeDao"></property> </bean> <bean id="departmentService" class="com.ssh.service.DepartmentService"> <property name="departmentDao" ref="departmentDao"></property> </bean> <bean id="employeeDao" class="com.ssh.dao.EmployeeDao"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <bean id="departmentDao" class="com.ssh.dao.DepartmentDao"> <property name="sessionFactory" ref="sessionFactory"></property> </bean>
主页面:
<a href="emp_input">添加</a>
点击添加后,跳转到emp_input Action,然后在Action中调用input方法,去加载Department信息。跳转到添加的页面
<result name="input">/WEB-INF/views/emp-input.jsp</result>
<s:form action="emp_save" method="post"> <s:textfield name="lastName" label="LastName"></s:textfield> <s:textfield name="email" label="Email"></s:textfield> <s:textfield name="birth" label="Birth"></s:textfield> //把部门的信息显示出来
<s:select list="#request.departments" listKey="id" listValue="departmentName" name="department.id" label="Department"></s:select> <s:submit></s:submit> </s:form>
点击提交submit的时候,到Action中。先去调用prepareSave()方法。创建一个Employee对象
public void prepareSave(){ model = new Employee(); }
然后调用getModel()方法,把Employee对象放到值栈的栈顶。由prepare拦截器把表单的值赋值给栈顶对象对应的属性。
private Employee model; public Employee getModel() { // TODO Auto-generated method stub return model; }
需要先继承ModelDriven<Employee>,Preparable拦截器。
//这是Preparable拦截器的方法 public void prepare() throws Exception { }
关于PrepareInterceptor
<pre name="code" class="java">public String doIntercept(ActionInvocation invocation) throws Exception { // 获取实例 Object action = invocation.getAction(); // 判断Action是否实现了Preparable接口 if (action instanceof Preparable) { try { String[] prefixes; // 根据当前拦截器firstCallPrepareDo(默认为false) // 属性确定prefixes if (firstCallPrepareDo) { prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX}; } else { prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX}; } // 若为false,则prefixes为:prepare、prepareDo // 调用前缀方法 PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof Exception) { throw (Exception) cause; } else if(cause instanceof Error) { throw (Error) cause; } else { throw e; } } // 根据当前拦截器的alwaysInvokePrepare(默认为true)决定是否调用Action的prepare方法 if (alwaysInvokePrepare) { ((Preparable) action).prepare(); } } return invocation.invoke(); } }
对上面程序中的PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);进行分析:
<pre name="code" class="java">public String doIntercept(ActionInvocation invocation) throws Exception { // 获取实例 Object action = invocation.getAction(); // 判断Action是否实现了Preparable接口 if (action instanceof Preparable) { try { String[] prefixes; // 根据当前拦截器firstCallPrepareDo(默认为false) // 属性确定prefixes if (firstCallPrepareDo) { prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX}; } else { prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX}; } // 若为false,则prefixes为:prepare、prepareDo // 调用前缀方法 PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof Exception) { throw (Exception) cause; } else if(cause instanceof Error) { throw (Error) cause; } else { throw e; } } // 根据当前拦截器的alwaysInvokePrepare(默认为true)决定是否调用Action的prepare方法 if (alwaysInvokePrepare) { ((Preparable) action).prepare(); } } return invocation.invoke(); } }
对上面的程序中的 Method method = getPrefixedMethod(prefixes, methodName, action);方法进行分析:
public String doIntercept(ActionInvocation invocation) throws Exception { // 获取实例 Object action = invocation.getAction(); // 判断Action是否实现了Preparable接口 if (action instanceof Preparable) { try { String[] prefixes; // 根据当前拦截器firstCallPrepareDo(默认为false) // 属性确定prefixes if (firstCallPrepareDo) { prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX}; } else { prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX}; } // 若为false,则prefixes为:prepare、prepareDo // 调用前缀方法 PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof Exception) { throw (Exception) cause; } else if(cause instanceof Error) { throw (Error) cause; } else { throw e; } } // 根据当前拦截器的alwaysInvokePrepare(默认为true)决定是否调用Action的prepare方法 if (alwaysInvokePrepare) { ((Preparable) action).prepare(); } } return invocation.invoke(); } }
(1)若Action实现了Prepare接口,则Struts将尝试执行prepare[ActionMethodName]方法,
<interceptors> <interceptor-stack name="sshStack"> <interceptor-ref name="paramsPrepareParamsStack"> <param name="prepare.alwaysInvokePrepare">false</param> </interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="sshStack"></default-interceptor-ref>
然后写保存方法
public String save(){ System.out.println(model); employeeService.saveOrUpdate(model); return SUCCESS; }
service
public void saveOrUpdate(Employee employee){ employeeDao.saveOrUpdate(employee); }
dao
public void saveOrUpdate(Employee employee){ getSession().saveOrUpdate(employee); }
保存完 后重定向到显示所有的页面
<result name="success" type="redirect">/emp_list</result>
因为页面上的日期是String类型。转换成Date类型
自己写个转换的类。然后java.util.Date xwork-conversion.properties 指向这个类 java.util.Date=com.ssh.converters.SSHDateConverter
package com.ssh.converters; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import org.apache.struts2.util.StrutsTypeConverter; public class SSHDateConverter extends StrutsTypeConverter { private DateFormat dateFormat; { dateFormat = new SimpleDateFormat("yyyy-MM-dd"); } @Override public Object convertFromString(Map context, String[] values, Class toClass) { if(toClass == Date.class){ try { return dateFormat.parse(values[0]); } catch (ParseException e) { e.printStackTrace(); } } return null; } @Override public String convertToString(Map context, Object o) { if(o instanceof Date){ return dateFormat.format((Date)o); } return null; } }
**:用ajax去校验姓名
<head> <title>添加员工</title> <script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script> <script type="text/javascript"> $(function(){ $(":input[name=lastName]").change(function(){ var val = $(this).val(); val = $.trim(val); var $this = $(this); if(val != ""){ //把当前节点后面的所有 font 兄弟节点删除 $this.nextAll("font").remove(); var url = "emp_validateLastName"; var args = {"lastName":val, "time":new Date()}; $.post(url, args, function(data){ //表示可用 if(data == "1"){ $this.after("<font color='green'>LastName可用!</font>"); } //不可用 else if(data == "0"){ $this.after("<font color='red'>LastName不可用!</font>"); } //服务器错误 else{ alert("服务器错误!"); } }); }else{ alert("lastName 不能为空"); $(this).val(""); $this.focus(); } }); }) </script> </head>
在Action中写个lastName属性。页面上输入的值就传入到这个属性中。然后这个属性传入到get方法中。
private String lastName; public void setLastName(String lastName) { this.lastName = lastName; } public String validateLastName() throws UnsupportedEncodingException{ if(employeeService.lastNameIsValid(lastName)){ inputStream = new ByteArrayInputStream("1".getBytes("UTF-8")); }else{ inputStream = new ByteArrayInputStream("0".getBytes("UTF-8")); } return "ajax-success"; }
service
//是一个boolean。lastName 是不是为 null 如果为null,为true 就是可用。不为空,false 不可用。
//是一个只读的方法。在事务中添加上 <tx:method name="lastNameIsValid" read-only="true"/>
public boolean lastNameIsValid(String lastName){
return employeeDao.getEmployeeByLastName(lastName) == null;
}
dao
public Employee getEmployeeByLastName(String lastName){ String hql="FROM Employee e where e.lastName=?"; Query query=getSession().createQuery(hql).setString(0, lastName); Employee employee=(Employee) query.uniqueResult(); return employee; }