本文目录导读:

我将为您提供一个完整的SSH(Struts2 + Spring + Hibernate)框架整合案例,这是一个经典的企业级Java Web开发组合。
项目结构
ssh-integration-demo
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ ├── action
│ │ │ │ ├── UserAction.java
│ │ │ │ └── BaseAction.java
│ │ │ ├── service
│ │ │ │ ├── UserService.java
│ │ │ │ └── impl
│ │ │ │ └── UserServiceImpl.java
│ │ │ ├── dao
│ │ │ │ ├── UserDao.java
│ │ │ │ └── impl
│ │ │ │ └── UserDaoImpl.java
│ │ │ ├── entity
│ │ │ │ └── User.java
│ │ │ └── util
│ │ │ └── HibernateUtil.java
│ │ ├── resources
│ │ │ ├── applicationContext.xml
│ │ │ ├── struts.xml
│ │ │ ├── hibernate.cfg.xml
│ │ │ └── log4j.properties
│ │ └── webapp
│ │ ├── WEB-INF
│ │ │ └── web.xml
│ │ ├── index.jsp
│ │ └── jsp
│ │ ├── userList.jsp
│ │ └── addUser.jsp
└── pom.xml
Maven依赖配置 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<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>com.example</groupId>
<artifactId>ssh-integration-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<struts2.version>2.5.30</struts2.version>
<spring.version>5.3.20</spring.version>
<hibernate.version>5.6.10.Final</hibernate.version>
</properties>
<dependencies>
<!-- Struts2 -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>${struts2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>${struts2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-json-plugin</artifactId>
<version>${struts2.version}</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</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-web</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-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- 其他 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>ssh-demo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
</build>
</project>
实体类 (User.java)
package com.example.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "t_user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", nullable = false, unique = true, length = 50)
private String username;
@Column(name = "password", nullable = false, length = 100)
private String password;
@Column(name = "email", length = 100)
private String email;
@Column(name = "phone", length = 20)
private String phone;
@Column(name = "create_time")
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
@Column(name = "update_time")
@Temporal(TemporalType.TIMESTAMP)
private Date updateTime;
}
DAO层
UserDao.java
package com.example.dao;
import com.example.entity.User;
import java.util.List;
public interface UserDao {
User findById(Long id);
User findByUsername(String username);
List<User> findAll();
void save(User user);
void update(User user);
void delete(User user);
void deleteById(Long id);
long count();
}
UserDaoImpl.java
package com.example.dao.impl;
import com.example.dao.UserDao;
import com.example.entity.User;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Repository("userDao")
@Transactional
public class UserDaoImpl implements UserDao {
@Autowired
private SessionFactory sessionFactory;
@Autowired
private HibernateTemplate hibernateTemplate;
private Session getSession() {
return sessionFactory.getCurrentSession();
}
@Override
public User findById(Long id) {
return hibernateTemplate.get(User.class, id);
}
@Override
public User findByUsername(String username) {
Session session = getSession();
Query<User> query = session.createQuery(
"from User where username = :username", User.class);
query.setParameter("username", username);
return query.uniqueResult();
}
@Override
public List<User> findAll() {
return hibernateTemplate.loadAll(User.class);
}
@Override
public void save(User user) {
hibernateTemplate.save(user);
}
@Override
public void update(User user) {
hibernateTemplate.update(user);
}
@Override
public void delete(User user) {
hibernateTemplate.delete(user);
}
@Override
public void deleteById(Long id) {
User user = findById(id);
if (user != null) {
hibernateTemplate.delete(user);
}
}
@Override
public long count() {
Session session = getSession();
Query<Long> query = session.createQuery(
"select count(u) from User u", Long.class);
return query.uniqueResult();
}
}
Service层
UserService.java
package com.example.service;
import com.example.entity.User;
import java.util.List;
public interface UserService {
User findById(Long id);
User findByUsername(String username);
List<User> findAll();
void save(User user);
void update(User user);
void delete(User user);
void deleteById(Long id);
boolean login(String username, String password);
long count();
}
UserServiceImpl.java
package com.example.service.impl;
import com.example.dao.UserDao;
import com.example.entity.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@Transactional(readOnly = true)
public User findById(Long id) {
return userDao.findById(id);
}
@Override
@Transactional(readOnly = true)
public User findByUsername(String username) {
return userDao.findByUsername(username);
}
@Override
@Transactional(readOnly = true)
public List<User> findAll() {
return userDao.findAll();
}
@Override
public void save(User user) {
if (user.getCreateTime() == null) {
user.setCreateTime(new Date());
}
user.setUpdateTime(new Date());
userDao.save(user);
}
@Override
public void update(User user) {
user.setUpdateTime(new Date());
userDao.update(user);
}
@Override
public void delete(User user) {
userDao.delete(user);
}
@Override
public void deleteById(Long id) {
userDao.deleteById(id);
}
@Override
@Transactional(readOnly = true)
public boolean login(String username, String password) {
User user = userDao.findByUsername(username);
return user != null && user.getPassword().equals(password);
}
@Override
@Transactional(readOnly = true)
public long count() {
return userDao.count();
}
}
Action层
BaseAction.java
package com.example.action;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public abstract class BaseAction extends ActionSupport
implements ServletRequestAware, ServletResponseAware {
protected HttpServletRequest request;
protected HttpServletResponse response;
@Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
@Override
public void setServletResponse(HttpServletResponse response) {
this.response = response;
}
}
UserAction.java
package com.example.action;
import com.example.entity.User;
import com.example.service.UserService;
import com.opensymphony.xwork2.ActionContext;
import lombok.Data;
import lombok.EqualsAndHashCode;
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.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.Map;
@Data
@EqualsAndHashCode(callSuper = true)
@ParentPackage("struts-default")
@Namespace("/user")
public class UserAction extends BaseAction {
@Autowired
private UserService userService;
private Long id;
private User user;
private List<User> userList;
private String username;
private String password;
@Action(value = "list", results = {
@Result(name = SUCCESS, location = "/jsp/userList.jsp")
})
public String list() {
userList = userService.findAll();
return SUCCESS;
}
@Action(value = "add", results = {
@Result(name = SUCCESS, type = "redirect", location = "/user/list")
})
public String add() {
if (user != null) {
// 检查用户名是否已存在
if (userService.findByUsername(user.getUsername()) != null) {
addActionError("用户名已存在!");
return ERROR;
}
userService.save(user);
addActionMessage("用户添加成功!");
}
return SUCCESS;
}
@Action(value = "edit", results = {
@Result(name = SUCCESS, location = "/jsp/editUser.jsp"),
@Result(name = ERROR, type = "redirect", location = "/user/list")
})
public String edit() {
if (id != null) {
user = userService.findById(id);
if (user == null) {
addActionError("用户不存在!");
return ERROR;
}
}
return SUCCESS;
}
@Action(value = "update", results = {
@Result(name = SUCCESS, type = "redirect", location = "/user/list")
})
public String update() {
if (user != null && user.getId() != null) {
User existingUser = userService.findById(user.getId());
if (existingUser != null) {
existingUser.setEmail(user.getEmail());
existingUser.setPhone(user.getPhone());
if (user.getPassword() != null && !user.getPassword().isEmpty()) {
existingUser.setPassword(user.getPassword());
}
userService.update(existingUser);
addActionMessage("用户更新成功!");
}
}
return SUCCESS;
}
@Action(value = "delete", results = {
@Result(name = SUCCESS, type = "redirect", location = "/user/list")
})
public String delete() {
if (id != null) {
userService.deleteById(id);
addActionMessage("用户删除成功!");
}
return SUCCESS;
}
@Action(value = "login", results = {
@Result(name = SUCCESS, type = "redirect", location = "/user/list"),
@Result(name = "login", location = "/index.jsp")
})
public String login() {
if (username != null && password != null) {
if (userService.login(username, password)) {
ActionContext.getContext().getSession()
.put("currentUser", username);
return SUCCESS;
} else {
addActionError("用户名或密码错误!");
}
}
return "login";
}
@Action(value = "logout", results = {
@Result(name = SUCCESS, type = "redirect", location = "/index.jsp")
})
public String logout() {
Map<String, Object> session = ActionContext.getContext().getSession();
session.remove("currentUser");
return SUCCESS;
}
}
配置文件
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>SSH Integration Demo</display-name>
<!-- 配置Spring监听器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置Struts2过滤器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置字符编码过滤器 -->
<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>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
applicationContext.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"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 扫描注解 -->
<context:component-scan base-package="com.example">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 读取数据库配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置数据源 (使用C3P0) -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- 连接池配置 -->
<property name="minPoolSize" value="5"/>
<property name="maxPoolSize" value="20"/>
<property name="initialPoolSize" value="5"/>
<property name="acquireIncrement" value="5"/>
<property name="maxIdleTime" value="1800"/>
<property name="checkoutTimeout" value="3000"/>
</bean>
<!-- 配置SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan">
<list>
<value>com.example.entity</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.current_session_context_class">
org.springframework.orm.hibernate5.SpringSessionContext
</prop>
</props>
</property>
</bean>
<!-- 配置HibernateTemplate -->
<bean id="hibernateTemplate"
class="org.springframework.orm.hibernate5.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置AOP切面 -->
<aop:config>
<aop:pointcut id="servicePointcut"
expression="execution(* com.example.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
</aop:config>
</beans>
struts.xml
<?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>
<!-- 常量配置 -->
<constant name="struts.devMode" value="true"/>
<constant name="struts.i18n.encoding" value="UTF-8"/>
<constant name="struts.ui.theme" value="simple"/>
<constant name="struts.convention.default.parent.package" value="struts-default"/>
<constant name="struts.convention.package.locators" value="action"/>
<!-- 配置spring集成 -->
<constant name="struts.objectFactory" value="spring"/>
<!-- 配置包 -->
<package name="default" extends="struts-default">
<!-- 配置全局结果 -->
<global-results>
<result name="error">/error.jsp</result>
<result name="input">/error.jsp</result>
</global-results>
<!-- Action配置(也可使用注解方式) -->
<action name="userAction" class="userAction">
<result name="success">/jsp/userList.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
</struts>
db.properties
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssh_demo?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8 jdbc.username=root jdbc.password=yourpassword
log4j.properties
log4j.rootLogger=INFO, console, file
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=logs/ssh-demo.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
# Hibernate日志
log4j.logger.org.hibernate=INFO
log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.type=TRACE
# Spring日志
log4j.logger.org.springframework=INFO
JSP页面
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">SSH框架整合示例</title>
<style>
body {
font-family: 'Microsoft YaHei', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
margin: 0;
padding: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.login-box {
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
width: 400px;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #555;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 5px;
box-sizing: border-box;
font-size: 14px;
}
input[type="text"]:focus,
input[type="password"]:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 5px rgba(102,126,234,0.3);
}
.btn-submit {
width: 100%;
padding: 12px;
background: #667eea;
color: white;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}
.btn-submit:hover {
background: #5a67d8;
}
.error-message {
color: red;
margin-bottom: 15px;
text-align: center;
}
.message {
color: #28a745;
margin-bottom: 15px;
text-align: center;
}
</style>
</head>
<body>
<div class="login-box">
<h1>SSH整合示例</h1>
<c:if test="${not empty errorMessages}">
<div class="error-message">
<c:forEach items="${errorMessages}" var="error">
${error}<br/>
</c:forEach>
</div>
</c:if>
<c:if test="${not empty actionMessages}">
<div class="message">
<c:forEach items="${actionMessages}" var="msg">
${msg}<br/>
</c:forEach>
</div>
</c:if>
<form action="/user/login" method="post">
<div class="form-group">
<label>用户名</label>
<input type="text" name="username" required/>
</div>
<div class="form-group">
<label>密码</label>
<input type="password" name="password" required/>
</div>
<button type="submit" class="btn-submit">登录</button>
</form>
<div style="text-align: center; margin-top: 20px;">
<a href="/user/list" style="color: #667eea; text-decoration: none;">直接查看用户列表</a>
</div>
</div>
</body>
</html>
userList.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">用户列表</title>
<style>
body {
font-family: 'Microsoft YaHei', sans-serif;
background: #f5f6fa;
margin: 0;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 30px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
h1 {
color: #333;
margin: 0;
}
.btn {
padding: 10px 20px;
border-radius: 5px;
text-decoration: none;
color: white;
transition: background 0.3s;
}
.btn-primary {
background: #667eea;
}
.btn-primary:hover {
background: #5a67d8;
}
.btn-danger {
background: #e74c3c;
padding: 5px 10px;
font-size: 12px;
}
.btn-danger:hover {
background: #c0392b;
}
.btn-warning {
background: #f39c12;
padding: 5px 10px;
font-size: 12px;
}
.btn-warning:hover {
background: #d68910;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #e0e0e0;
}
th {
background: #f8f9fa;
color: #333;
font-weight: 600;
}
tr:hover {
background: #f5f6fa;
}
.alert {
padding: 12px;
border-radius: 5px;
margin-bottom: 20px;
}
.alert-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.logout-link {
color: #667eea;
text-decoration: none;
margin-left: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>用户列表</h1>
<div>
<c:if test="${sessionScope.currentUser != null}">
<span>欢迎 <strong>${sessionScope.currentUser}</strong></span>
</c:if>
<a href="/user/add" class="btn btn-primary">添加用户</a>
<a href="/user/logout" class="logout-link">退出</a>
</div>
</div>
<c:if test="${not empty actionMessages}">
<div class="alert alert-success">
<c:forEach items="${actionMessages}" var="msg">
${msg}<br/>
</c:forEach>
</div>
</c:if>
<c:if test="${not empty errorMessages}">
<div class="alert alert-error">
<c:forEach items="${errorMessages}" var="error">
${error}<br/>
</c:forEach>
</div>
</c:if>
<table>
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>电话</th>
<th>创建时间</th>
<th>更新时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<c:forEach items="${userList}" var="u">
<tr>
<td>${u.id}</td>
<td>${u.username}</td>
<td>${u.email}</td>
<td>${u.phone}</td>
<td><fmt:formatDate value="${u.createTime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
<td><fmt:formatDate value="${u.updateTime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
<td>
<a href="/user/edit?id=${u.id}" class="btn btn-warning">编辑</a>
<a href="/user/delete?id=${u.id}" class="btn btn-danger"
onclick="return confirm('确定要删除该用户吗?')">删除</a>
</td>
</tr>
</c:forEach>
<c:if test="${empty userList}">
<tr>
<td colspan="7" style="text-align: center; color: #999;">
暂无用户数据
</td>
</tr>
</c:if>
</tbody>
</table>
<div style="margin-top: 20px; color: #666;">
共 ${fn:length(userList)} 条记录
</div>
</div>
</body>
</html>
addUser.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">添加用户</title>
<style>
body {
font-family: 'Microsoft YaHei', sans-serif;
background: #f5f6fa;
margin: 0;
padding: 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
background: white;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 30px;
}
h1 {
color: #333;
margin-bottom: 30px;
text-align: center;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin