本文介绍将DAO层引入struts,并以注册界面作为例子。内容来自《Java Web轻量级框架项目化教程》
第一篇主要讲了struts的基本原理,由于当时用的是struts1,比较老,这次用struts2,需要重新去官网下包,但struts2基本的思路和1一样,很容易理解。
引入DAO层
可以看到第一篇的数据库操作是在action类里面的,不可复用,于是引入了dao层,定义连接数据库的接口和基本类,然后再dao.impl包里面对接口进行实现(addUser等),这样,在action类里面就可以直接创建一个已经实现的dao.impl类对象,然后直接用addUser进行加用户操作,可以完全忽略数据库那边是怎么处理的。
基本代码
下图中黄色框内为注册功能所需的代码文件。
RegisterAction.java 执行业务操作
package com.digital.action;
import com.digital.dao.UserDAO;
import com.digital.dao.impl.UserDAOImpl;
import com.digital.entity.User;
import com.opensymphony.xwork2.ActionSupport;
public class RegisterAction extends ActionSupport {
private User user;//给了setter和getter后,对应表单里的name属性值,该Action会自动调用setter方法创建一个实例,里面数据就是表单里的。
private String repassword;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getRepassword() {
return repassword;
}
public void setRepassword(String repassword) {
this.repassword = repassword;
}
public String reg() throws Exception {
UserDAO ud=new UserDAOImpl();
int result=ud.addUser(user);
String back;
if (result!=0) {
back="success";
}else{
back="input";
}
return back;
}
}
BaseDAO.java 提供复用性,数据库连接和释放操作。
package com.digital.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class BaseDAO {
public Connection getConnection(){
Connection conn=null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/digital?useSSL=true","root","admin");
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
public void closeAll(Connection conn,PreparedStatement pstmt,ResultSet rs){
if (rs!=null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (pstmt!=null) {
try {
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (conn!=null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
UserDAO.java 定义用户操纵数据库的接口,实现由dao.impl里面的类实现。
package com.digital.dao;
import com.digital.entity.User;
public interface UserDAO {
public int addUser(User user);//添加一个用户
}
UserDAOImpl.java 对UserDAO接口的具体实现。
package com.digital.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import com.digital.dao.BaseDAO;
import com.digital.dao.UserDAO;
import com.digital.entity.User;
public class UserDAOImpl extends BaseDAO implements UserDAO {
Connection conn=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
@Override
public int addUser(User user) {
int result=0;
String sql="insert into user_info(userName,password,realName,address,email) values(?,?,?,?,?)";
try {
conn=this.getConnection();
pstmt=conn.prepareStatement(sql);
pstmt.setString(1, user.getUserName());
pstmt.setString(2, user.getPassword());
pstmt.setString(3, user.getRealName());
pstmt.setString(4, user.getAddress());
pstmt.setString(5, user.getEmail());
result=pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
closeAll(conn, pstmt, rs);
}
return result;
}
}
User.java 实体类,属性和setter以及getter方法。
package com.digital.entity;
import java.util.Date;
public class User {
private int id;
private String userName;
private String password;
private String realName;
private String sex;
private String address;
private String question;
private String answer;
private String email;
private String favorate;
private int score;
private Date regDate;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
public String getAnswer() {
return answer;
}
public void setAnswer(String answer) {
this.answer = answer;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFavorate() {
return favorate;
}
public void setFavorate(String favorate) {
this.favorate = favorate;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public Date getRegDate() {
return regDate;
}
public void setRegDate(Date regDate) {
this.regDate = regDate;
}
}
struts.xml
struts2的配置文件和1略有区别。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="digital" namespace="/" extends="struts-default">
<action name="register" class="com.digital.action.RegisterAction"
method = "reg"><!--指定方法,否则默认execute-->
<result name="success">/userinfo.jsp</result>
<result name="input">/reg.jsp</result>
</action>
</package>
</struts>
最后,web.xml
<?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"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>digital</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<filter>
<!-- 过滤器名 -->
<filter-name>struts2</filter-name>
<!-- 配置Struts 2的核心控制器的实现类 -->
<filter-class>
org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<!-- 要拦截的请求 -->
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
注意点:如果在Action配置中省略了class属性,则代表访问ActionSupport类,其execute方法是直接返回success的。
数据校验 方法一:validate
如果表单有明显的问题(可以不用连接数据库就可以知道错了,如两次密码不一样),那么直接调用action里面的方法,去数据库查一遍显然浪费时间了,Struts2提供了对表单进行验证的方法,我们编写自己的action时只需继承一下,然后改成自己所需要的样子就行了,如在RegisterAction里面添加:
@Override
public void validate() {
if(user.getUserName().length()==0)
addFieldError("username", "用户名不能为空");
if(user.getPassword().length()==0)
addFieldError("password", "密码不能为空!");
if(!(user.getPassword().equals(this.getRepassword())))
addFieldError("repassword", "确认密码和登录密码不一致!");
super.validate();
}
addFieldError后,在reg.jsp中添加:
<font color="red" size="3px"><s:fielderror/></font>
就可以看到类似这样的效果:
更多关于error显示的内容可以看看:http://www.cnblogs.com/wangyp/archive/2011/07/13/2104828.html
数据校验 方法二:验证框架
验证的代码用单独的配置文件完成,而不是放在action里面。
RegisterAction-validation.xml,放在和RegisterAction同样的目录下。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
<field name="user.userName">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message key="userName.null"></message>
</field-validator>
<field-validator type="stringlength">
<param name="maxLength">10</param>
<param name="minLength">4</param>
<message key="userName.length"></message>
</field-validator>
</field>
<field name="user.password">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message key="password.null"></message>
</field-validator>
<field-validator type="stringlength">
<param name="minLength">4</param>
<message key="password.length"></message>
</field-validator>
</field>
<field name="repassword">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message key="repassword.null"></message>
</field-validator>
<field-validator type="fieldexpression">
<param name="expression">user.password==repassword</param>
<message key="repassword.same"></message>
</field-validator>
</field>
<field name="user.realName">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message key="realName.null"></message>
</field-validator>
<field-validator type="stringlength">
<param name="maxLength">4</param>
<param name="minLength">2</param>
<message>真实姓名长度在${minLength}到${maxLength}之间</message>
</field-validator>
</field>
<field name="user.address">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message key="address.null"></message>
</field-validator>
<field-validator type="stringlength">
<param name="maxLength">30</param>
<message>通信地址长度不能超过${maxLength}</message>
</field-validator>
</field>
<field name="user.email">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message key="email.null"></message>
</field-validator>
<field-validator type="email">
<message>电子邮箱格式不符合要求</message>
</field-validator>
</field>
</validators>
如果有一些复杂的校验要求,可以用validate方法,拦截器顺序是:先由validation拦截器调用验证框架,再由workflow执行编码验证。
至此,我们知道Struts在处理action的时候,可以通过addFielderror,ActionContext等将“额外信息”保存下来,然后jsp中有特定的方式显示,如
ActionContext ac = ActionContext.getContext();
ac.getSession().put("name","mike");
jsp中:
${sessionScope.name}
提取session范围的信息。
这篇博文从源码角度分析了ActionContext类的功能,点击,简单来说就是ActionContext保存了一个Map,里面存放着一些信息,
{message}是按照pageScope,requestScope,sessionScope,applicationScope寻找,直到找到为止。我做了个测试,
1. private变量
只有用${request.message}
时才会找到Action类里面的,说明action里面私有变量加的地方默认是request的,而且其他范围(包括applicationScope)都访问不到。
2. actionContext.getApplication().put("CURRENT_USER", usr);
往application范围添加数据,但是session也能访问到。
那么,在action里面的private变量是什么时候加到哪里的呢?