视频连接
资料连接
『架构』其实就是『项目的结构』,只是因为架构是一个更大的词,通常用来形容比较大规模事物的结构。
单一架构也叫『all-in-one
』结构,就是所有代码、配置文件、各种资源都在同一个工程。
https://mvnrepository.com/
确定技术选型:确定我们项目中要使用哪些技术
到 mvnrepository 网站搜索具体技术对应的具体依赖信息
确定这个技术使用哪个版本的依赖
在实际使用中检验所有依赖信息是否都正常可用
确定技术选型、组建依赖列表、项目划分模块……等等这些操作其实都属于架构设计的范畴。
- 项目本身所属行业的基本特点
- 项目具体的功能需求
- 项目预计访问压力程度
- 项目预计将来需要扩展的功能
- 设计项目总体的体系结构
mysql:mysql-connector-java:5.1.37
com.alibaba:druid:1.2.8
commons-dbutils:commons-dbutils:1.6
javax.servlet:javax.servlet-api:3.1.0
org.thymeleaf:thymeleaf:3.0.11.RELEASE
junit:junit:4.12
ch.qos.logback:logback-classic:1.2.3
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.37version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.8version>
dependency>
<dependency>
<groupId>commons-dbutilsgroupId>
<artifactId>commons-dbutilsartifactId>
<version>1.6version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleafartifactId>
<version>3.0.11.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
<scope>testscope>
dependency>
pro05-demo-all-in-one
项目的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.0modelVersion>
<groupId>com.atgugui.mavengroupId>
<artifactId>pro05-demo-all-in-oneartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.37version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.8version>
dependency>
<dependency>
<groupId>commons-dbutilsgroupId>
<artifactId>commons-dbutilsartifactId>
<version>1.6version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleafartifactId>
<version>3.0.11.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
dependencies>
project>
package 功能 | package 名称 |
---|---|
主包 | com.atguigu.imperial.court |
子包[实体类] | com.atguigu.imperial.court.entity |
子包[Servlet基类/父类包] | com.atguigu.imperial.court.servlet.base |
子包[Servlet模块包] | com.atguigu.imperial.court.servlet.module |
子包[Service接口包] | com.atguigu.imperial.court.service.api |
子包[Service实现类包] | com.atguigu.imperial.court.service.impl |
子包[Dao接口包] | com.atguigu.imperial.court.dao.api |
子包[Dao实现类包] | com.atguigu.imperial.court.dao.impl |
子包[Filter] | com.atguigu.imperial.court.filter |
子包[异常类包] | com.atguigu.imperial.court.exception |
子包[工具类] | com.atguigu.imperial.court.util |
子包[测试类] | com.atguigu.imperial.court.test |
# 创建数据库
create database db_imperial_court;
# 进入数据库
use db_imperial_court;
# 创建表 t_emp
create table t_emp
(
emp_id int primary key auto_increment,
emp_name varchar(100) not null,
emp_position varchar(100) not null,
login_account varchar(100) not null unique,
login_password varchar(100) not null
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
# 插入数据
insert into t_emp(emp_name, emp_position, login_account, login_password)
values ('爱新觉罗·玄烨', 'emperor', 'xiaoxuanzi1654', '25325C896624D444B2E241807DCAC98B'), # 16540504
('纳兰明珠', 'minister', 'brightball1635', 'A580D0EF93C22036C859E194C14CB777'), # 16351119
('赫舍里·索额图', 'minister', 'tutu1636', 'E40FD7D49B8B7EF46F47407D583C3538'); # 17030921
# 创建表 t_memorials
create table t_memorials
(
memorials_id int primary key auto_increment,
memorials_title char(100) ,
memorials_content varchar(5000),
memorials_emp int,
memorials_create_time char(100),
feedback_time char(100),
feedback_content varchar(1000),
memorials_status int not null
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
# 插入数据
insert into t_memorials(memorials_title,
memorials_content,
memorials_emp,
memorials_create_time,
feedback_time,
feedback_content,
memorials_status)
values ('浙江巡抚奏钱塘堤决口疏', '皇上啊,不好啦!钱塘江发大水啦!堤坝冲毁啦!您看这咋弄啊!', 2, '1690-05-07', null, null, 0),
('左都御史参鳌拜圈地疏', '皇上啊,鳌拜这厮不是东西呀!占老百姓的地哇!还打人呀!您看咋弄啊!', 3, '1690-04-14', null, null, 0),
('都察院劾吴三桂不臣疏', '皇上啊,不得了啦!吴三桂那孙子想造反呀!', 2, '1693-11-18', null, null, 0),
('兵部奏准噶尔犯境疏', '皇上啊,不得了啦!葛尔丹要打过来了呀!', 3, '1693-11-18', null, null, 0),
('朝鲜使臣朝拜事宜呈皇上御览', '皇上啊!朝鲜国的人要来啦!咱们请他们吃猪肉炖粉条子吧!', 2, '1680-06-11', null, null, 0),
('英吉利炮舰购买事宜疏', '皇上啊!英国的小船船咱们买多少啊?', 3, '1680-06-12', null, null, 0),
('劾杭州织造贪墨疏', '皇上啊!杭州织造有问题啊!', 2, '1680-06-13', null, null, 0),
('禀畅春园落成疏', '皇上啊!畅春园修好了哇!您啥时候过来看看呀!', 3, '1680-06-14', null, null, 0),
('请旨木兰秋狝疏', '皇上啊!秋天到啦,又该打猎啦!', 2, '1680-06-15', null, null, 0),
('核准西北军饷银两疏', '皇上啊!您看看这钱数算的对不对呀!', 3, '1680-06-16', null, null, 0),
('请旨裁撤三藩疏', '皇上啊!咱们不裁撤三藩就芭比Q了哇!', 2, '1680-06-17', null, null, 0),
('蒙古王公进京朝拜疏', '皇上啊!蒙古王公要来啦!咱们请他们吃猪肉炖粉条子吧!', 3, '1680-06-18', null, null, 0),
('礼部请旨九阿哥赐名疏', '皇上啊!您看九阿哥该叫什么名字呀?', 2, '1680-06-19', null, null, 0),
('户部尚书请旨告老还乡疏', '皇上啊!臣想回家养老啦!您看看啥时候给臣把俸禄结一下啊!', 3, '1680-06-20', null, null, 0),
('查江宁织造贪墨疏', '皇上啊!江宁织造有问题啊!', 2, '1680-06-21', null, null, 0)
;
package com.atguigu.imperial.court.entity;
public class Emp {
private Integer empId;
private String empName;
private String empPosition;
private String loginAccount;
private String loginPassword;
//构造器 get/set 方法...
}
package com.atguigu.imperial.court.entity;
public class Memorials {
private Integer memorialsId;
private String memorialsTitle;
private String memorialsContent;
// 奏折摘要数据库没有,这里是为了配合页面显示
private String memorialsContentDigest;
private Integer memorialsEmp;
// 员工姓名数据库没有,这里是为了配合页面显示
private String memorialsEmpName;
private String memorialsCreateTime;
private String feedbackTime;
private String feedbackContent;
private Integer memorialsStatus;
//构造器 get/set 方法...
}
说明:这是我们第一次用到 Maven 约定目录结构中的 resources
目录,这个目录存放各种配置文件。
jdbc.properties
文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db_imperial_court
username=root
password=123456
initialSize=10
maxActive=20
maxWait=10000
package com.atguigu.imperial.court.util;
import javax.sql.DataSource;
import java.io.InputStream;
import java.util.Properties;
import com.alibaba.druid.pool.DruidDataSourceFactory;
/**
* 功能1:从数据源获取数据库连接
* 功能2:从数据库获取到数据库连接后,绑定到本地线程(借助 ThreadLocal)
* 功能3:释放线程时和本地线程解除绑定
*/
public class JDBCUtils {
// 数据源成员变量设置成静态资源,保证对象的单例性;同时保证静态方法中可以访问
private static DataSource dataSource;
// 在静态代码块中初始化数据源
static {
//操作思路分析:
// 从 jdbc.properties 文件中读取连接数据库的信息
// 为了保证程序代码的可移植性,需要基于一个确定的基准来读取这个文件
// 确定的基准: 类路径的根目录。resources 目录下的内容经过构建操作中打包操作后确定放在
// WEB-INFO/classes 目录下。 WEB-INFO/classes : 目录存放编译好的 *.class 字节码文件,
// 所以这个目录我们就称之为类路径。
// 类路径无论在本地运行还是在服务器端运行都是一个确定的基准。
//操作具体实现:
try {
//1、获取当前类的类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
//2、通过类加载器对象从类路径根目录下读取文件
InputStream stream = classLoader.getResourceAsStream("jdbc.properties");
//3、使用 Properties 类封装属性文件中的数据
Properties properties = new Properties();
properties.load(stream);
//4、根据 Properties 对象(已经封装了数据库连接信息)来创建数据源对象
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
//为了避免在真正抛出异常后, catch 块捕获到异常从而掩盖问题,
//这里将所捕获到的异常封装为运行异常继续抛出
throw new RuntimeException(e);
}
}
}
如果在每一个 Service 方法中都写下面代码,那么代码重复性就太高了:
try{
// 1、获取数据库连接
// 重要:要保证参与事务的多个数据库操作(SQL 语句)使用的是同一个数据库连接
Connection conn = JDBCUtils.getConnection();
// 2、核心操作
// ...
// 3、核心操作成功结束,可以提交事务
conn.commit();
}catch(Exception e){
// 4、核心操作抛出异常,必须回滚事务
conn.rollBack();
}finally{
// 5、释放数据库连接
JDBCUtils.releaseConnection(conn);
}
所谓『当前请求覆盖的 Servlet 方法、Service 方法、Dao 方法』其实就是 chain.doFilter(request, response)
间接调用的方法。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
try{
// 1、获取数据库连接
// 重要:要保证参与事务的多个数据库操作(SQL 语句)使用的是同一个数据库连接
Connection conn = JDBCUtils.getConnection();
// 重要操作:关闭自动提交功能
connection.setAutoCommit(false);
// 2、核心操作:通过 chain 对象放行当前请求
// 这样就可以保证当前请求覆盖的 Servlet 方法、Service 方法、Dao 方法都在同一个事务中。
// 同时各个请求都经过这个 Filter,所以当前事务控制的代码在这里只写一遍就行了,
// 避免了代码的冗余。
chain.doFilter(request, response);
// 3、核心操作成功结束,可以提交事务
conn.commit();
}catch(Exception e){
// 4、核心操作抛出异常,必须回滚事务
conn.rollBack();
}finally{
// 5、释放数据库连接
JDBCUtils.releaseConnection(conn);
}
}
通过 JDBCUtils 工具类获取到的 Connection 对象需要传递给 Dao 方法,让事务涉及到的所有 Dao 方法用的都是同一个 Connection 对象。
但是 Connection 对象无法通过 chain.doFilter()
方法以参数的形式传递过去。
所以从获取到 Connection 对象到使用 Connection 对象中间隔着很多不是我们自己声明的方法——我们无法决定它们的参数。
java.lang.ThreadLocal
方法名 | 功能 |
---|---|
set(T value) | 将数据绑定到当前线程 |
get() | 从当前线程获取已绑定的数据 |
remove() | 将数据从当前线程移除 |
// 由于 ThreadLocal 对象需要作为绑定数据时 k-v 对中的 key,所以要保证唯一性
// 加 static 声明为静态资源即可保证唯一性
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
在JDBCUtils
类添加getConnection()
方法
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
/**
* 工具方法:获取数据库连接并返回
* @return
*/
public static Connection getConnection(){
Connection connection = null;
try {
// 1、尝试从当前线程检查是否存在已经绑定的 Connection 对象
connection = threadLocal.get();
// 2、检查 Connection 对象是否为 null
if (connection == null) {
// 3、如果为 null,则从数据源获取数据库连接
connection = dataSource.getConnection();
// 4、获取到数据库连接后绑定到当前线程
threadLocal.set(connection);
}
} catch (SQLException e) {
e.printStackTrace();
// 为了调用工具方法方便,编译时异常不往外抛
// 为了不掩盖问题,捕获到的编译时异常封装为运行时异常抛出
throw new RuntimeException(e);
}
return connection;
}
在JDBCUtils
类添加releaseConnection()
方法
/**
* 释放数据库连接
*/
public static void releaseConnection(Connection connection) {
if (connection != null) {
try {
// 在数据库连接池中将当前连接对象标记为空闲
connection.close();
// 将当前数据库连接从当前线程上移除
threadLocal.remove();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
package com.atguigu.maven;
import com.atguigu.imperial.court.util.JDBCUtils;
import org.junit.Test;
import java.sql.Connection;
public class ImperialCourtTest {
@Test
public void testGetConnection() {
Connection connection = JDBCUtils.getConnection();
System.out.println("connection = " + connection);
JDBCUtils.releaseConnection(connection);
}
}
package com.atguigu.imperial.court.util;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import com.alibaba.druid.pool.DruidDataSourceFactory;
/**
* 功能1:从数据源获取数据库连接
* 功能2:从数据库获取到数据库连接后,绑定到本地线程(借助 ThreadLocal)
* 功能3:释放线程时和本地线程解除绑定
*/
public class JDBCUtils {
// 数据源成员变量设置成静态资源,保证对象的单例性;同时保证静态方法中可以访问
private static DataSource dataSource;
// 由于 ThreadLocal 对象需要作为绑定数据时 k-v 对中的 key,所以要保证唯一性
// 加 static 声明为静态资源即可保证唯一性
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
// 在静态代码块中初始化数据源
static {
//操作思路分析:
// 从 jdbc.properties 文件中读取连接数据库的信息
// 为了保证程序代码的可移植性,需要基于一个确定的基准来读取这个文件
// 确定的基准: 类路径的根目录。resources 目录下的内容经过构建操作中打包操作后确定放在
// WEB-INFO/classes 目录下。 WEB-INFO/classes : 目录存放编译好的 *.class 字节码文件,
// 所以这个目录我们就称之为类路径。
// 类路径无论在本地运行还是在服务器端运行都是一个确定的基准。
//操作具体实现:
try {
//1、获取当前类的类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
//2、通过类加载器对象从类路径根目录下读取文件
InputStream stream = classLoader.getResourceAsStream("jdbc.properties");
//3、使用 Properties 类封装属性文件中的数据
Properties properties = new Properties();
properties.load(stream);
//4、根据 Properties 对象(已经封装了数据库连接信息)来创建数据源对象
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
//为了避免在真正抛出异常后, catch 块捕获到异常从而掩盖问题,
//这里将所捕获到的异常封装为运行异常继续抛出
throw new RuntimeException(e);
}
}
/**
* 工具方法:获取数据库连接并返回
* @return
*/
public static Connection getConnection(){
Connection connection = null;
try {
// 1、尝试从当前线程检查是否存在已经绑定的 Connection 对象
connection = threadLocal.get();
// 2、检查 Connection 对象是否为 null
if (connection == null) {
// 3、如果为 null,则从数据源获取数据库连接
connection = dataSource.getConnection();
// 4、获取到数据库连接后绑定到当前线程
threadLocal.set(connection);
}
} catch (SQLException e) {
e.printStackTrace();
// 为了调用工具方法方便,编译时异常不往外抛
// 为了不掩盖问题,捕获到的编译时异常封装为运行时异常抛出
throw new RuntimeException(e);
}
return connection;
}
/**
* 释放数据库连接
*/
public static void releaseConnection(Connection connection) {
if (connection != null) {
try {
// 在数据库连接池中将当前连接对象标记为空闲
connection.close();
// 将当前数据库连接从当前线程上移除
threadLocal.remove();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
}
// DBUtils 工具包提供的数据库操作对象
private QueryRunner runner = new QueryRunner();
特别说明:在 BaseDao 方法中获取数据库连接但是不做释放,因为我们要在控制事务的 Filter 中统一释放。
package com.atguigu.imperial.court.dao;
import com.atguigu.imperial.court.util.JDBCUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
* BaseDao 类:所有 Dao 实现类的基类/父类
* @param 实体类的类型
*/
public class BaseDao<T> {
// DBUtils 工具包提供的数据库操作对象
private QueryRunner runner = new QueryRunner();
/**
* 查询单个对象
* @param sql 执行查询的 sql 语句
* @param entityClass 实体类对应的 Class 对象
* @param parameters 传给 SQL 语句的参数
* @return 查询到的实体类对象
*/
public T getSingleBean(String sql,Class<T> entityClass,Object ... parameters){
try {
// 获取数据库的连接
Connection connection = JDBCUtils.getConnection();
return runner.query(connection,sql,new BeanHandler<T>(entityClass),parameters);
}catch (Exception e){
e.printStackTrace();
// 如果真的抛出异常,则将编译异常封装为运行异常抛出
new RuntimeException(e);
}
return null;
}
/**
* 查询返回多个对象的方法
* @param sql 执行查询操作的 SQL 语句
* @param entityClass 实体类的 Class 对象
* @param parameters SQL 语句的参数
* @return 查询结果
*/
public List<T> getBeanList(String sql, Class<T> entityClass, Object ... parameters) {
try {
// 获取数据库连接
Connection connection = JDBCUtils.getConnection();
return runner.query(connection, sql, new BeanListHandler<T>(entityClass), parameters);
} catch (SQLException e) {
e.printStackTrace();
// 如果真的抛出异常,则将编译时异常封装为运行时异常抛出
new RuntimeException(e);
}
return null;
}
/**
* 通用的增删改方法,insert、delete、update 操作都可以用这个方法
* @param sql 执行操作的 SQL 语句
* @param parameters SQL 语句的参数
* @return 受影响的行数
*/
public int update(String sql, Object ... parameters) {
try {
Connection connection = JDBCUtils.getConnection();
int affectedRowNumbers = runner.update(connection, sql, parameters);
return affectedRowNumbers;
} catch (SQLException e) {
e.printStackTrace();
// 如果真的抛出异常,则将编译时异常封装为运行时异常抛出
new RuntimeException(e);
return 0;
}
}
}
在ImperialCourtTest
类下进行测试
private BaseDao<Emp> baseDao = new BaseDao<Emp>();
@Test
public void testGetSingleBean() {
String sql = "select emp_id empId,emp_name empName,emp_position empPosition,login_account loginAccount,login_password loginPassword from t_emp where emp_id=?";
Emp emp = baseDao.getSingleBean(sql, Emp.class, 1);
System.out.println("emp = " + emp);
}
@Test
public void testGetBeanList() {
String sql = "select emp_id empId,emp_name empName,emp_position empPosition,login_account loginAccount,login_password loginPassword from t_emp";
List<Emp> empList = baseDao.getBeanList(sql, Emp.class);
for (Emp emp : empList) {
System.out.println("emp = " + emp);
}
}
@Test
public void testUpdate() {
String sql = "update t_emp set emp_position=? where emp_id=?";
String empPosition = "emperor";
String empId = "3";
int affectedRowNumber = baseDao.update(sql, empPosition, empId);
System.out.println("affectedRowNumber = " + affectedRowNumber);
}
创建接口和实现类如下:
package com.atguigu.imperial.court.filter;
import com.atguigu.imperial.court.util.JDBCUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
public class TransactionFilter implements Filter {
// 声明集合保存静态资源扩展名
private static Set<String> staticResourceExtNameSet;
static {
staticResourceExtNameSet = new HashSet<String>();
staticResourceExtNameSet.add(".png");
staticResourceExtNameSet.add(".jpg");
staticResourceExtNameSet.add(".css");
staticResourceExtNameSet.add(".js");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 前置操作:排除静态资源
HttpServletRequest request = (HttpServletRequest) servletRequest;
String servletPath = request.getServletPath();
if (servletPath.contains(".")) {
String extName = servletPath.substring(servletPath.lastIndexOf("."));
if (staticResourceExtNameSet.contains(extName)) {
// 如果检测到当前请求确实是静态资源,则直接放行,不做事务操作
filterChain.doFilter(servletRequest, servletResponse);
// 当前方法立即返回
return ;
}
}
Connection connection = null;
try{
// 1、获取数据库连接
connection = JDBCUtils.getConnection();
// 重要操作:关闭自动提交功能
connection.setAutoCommit(false);
// 2、核心操作
filterChain.doFilter(servletRequest, servletResponse);
// 3、提交事务
connection.commit();
}catch (Exception e) {
try {
// 4、回滚事务
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
// 页面显示:将这里捕获到的异常发送到指定页面显示
// 获取异常信息
String message = e.getMessage();
// 将异常信息存入请求域
request.setAttribute("systemMessage", message);
// 将请求转发到指定页面
request.getRequestDispatcher("/").forward(request, servletResponse);
}finally {
// 5、释放数据库连接
JDBCUtils.releaseConnection(connection);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
注意:需要首先将当前工程改成 Web
工程。
在pom
文件添加:
<packaging>warpackaging>
然后在idea工具操作,如下:
在web.xml
文件添加filter
标签
<filter>
<filter-name>txFilterfilter-name>
<filter-class>com.atguigu.imperial.court.filter.TransactionFilterfilter-class>
filter>
<filter-mapping>
<filter-name>txFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
在程序执行的过程中,必须让所有 catch 块都把编译时异常转换为运行时异常抛出;如果不这么做,在 TransactionFilter 中 catch 就无法捕获到底层抛出的异常,那么该回滚的时候就无法回滚。
由于诸多操作都是在使用同一个数据库连接,那么中间任何一个环节释放数据库连接都会导致后续操作无法正常完成。
TemplateEngine
对象(模板引擎)TemplateEngine
对象使用的都是同一个,所以在初始化阶段准备好假设有下列页面地址:
/WEB-INF/pages/apple.html
/WEB-INF/pages/banana.html
/WEB-INF/pages/orange.html
/WEB-INF/pages/grape.html
/WEB-INF/pages/egg.html
这样的地址可以直接访问到页面本身,我们称之为:物理视图。而将物理视图中前面、后面的固定内容抽取出来,让每次请求指定中间变化部分即可,那么中间变化部分就叫:逻辑视图。
为了简化视图页面处理过程,我们将 Thymeleaf 模板引擎的初始化和请求处理过程封装到一个 Servlet 基类中:ViewBaseServlet
。以后负责具体模块业务功能的 Servlet 继承该基类即可直接使用。
package com.atguigu.imperial.court.servlet.base;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 处理视图模板文件的 Servlet 基类/父类
*/
public class ViewBaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
@Override
public void init() throws ServletException {
// 1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();
// 2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
// 3.给解析器对象设置参数
// ①HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
// ④设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60 * 1000L);
// ⑤设置是否缓存
templateResolver.setCacheable(true);
// ⑥设置服务器端编码方式
templateResolver.setCharacterEncoding("utf-8");
// 4.创建模板引擎对象
templateEngine = new TemplateEngine();
// 5.给模板引擎对象设置模板解析器
templateEngine.setTemplateResolver(templateResolver);
}
//逻辑视图
protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1.设置响应体内容类型和字符集
resp.setContentType("text/html;charset=UTF-8");
// 2.创建WebContext对象
WebContext webContext = new WebContext(req, resp, getServletContext());
// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
}
}
特别提醒:这个类不需要掌握,因为以后都被框架封装了,我们现在只是暂时用一下。
<context-param>
<param-name>view-prefixparam-name>
<param-value>/WEB-INF/pages/param-value>
context-param>
<context-param>
<param-name>view-suffixparam-name>
<param-value>.htmlparam-value>
context-param>
传送门
doGet()
方法:处理 GET 请求doPost()
方法:处理 POST 请求特别提醒:为了配合 TransactionFilter
实现事务控制,捕获的异常必须抛出。
package com.atguigu.imperial.court.servlet.base;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
public class ModelBaseServlet extends ViewBaseServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 在doGet()方法中调用doPost()方法,这样就可以在doPost()方法中集中处理所有请求
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.在所有request.getParameter()前面设置解析请求体的字符集
request.setCharacterEncoding("UTF-8");
// 2.从请求参数中获取method对应的数据
String method = request.getParameter("method");
// 3.通过反射调用method对应的方法
// ①获取Class对象
Class<? extends ModelBaseServlet> clazz = this.getClass();
try {
// ②获取method对应的Method对象
Method methodObject = clazz.getDeclaredMethod(method, HttpServletRequest.class, HttpServletResponse.class);
// ③打开访问权限
methodObject.setAccessible(true);
// ④通过Method对象调用目标方法
methodObject.invoke(this, request, response);
} catch (Exception e) {
e.printStackTrace();
// **重要提醒:为了配合 TransactionFilter 实现事务控制,捕获的异常必须抛出。**
throw new RuntimeException(e);
}
}
}
package com.atguigu.imperial.court.util;
public class ImperialCourtConst {
public static final String LOGIN_FAILED_MESSAGE = "账号、密码错误,不可进宫!";
public static final String ACCESS_DENIED_MESSAGE = "宫闱禁地,不得擅入!";
}
package com.atguigu.imperial.court.util;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Util {
/**
* 针对明文字符串执行MD5加密
* @param source
* @return
*/
public static String encode(String source) {
// 1.判断明文字符串是否有效
if (source == null || "".equals(source)) {
throw new RuntimeException("用于加密的明文不可为空");
}
// 2.声明算法名称
String algorithm = "md5";
// 3.获取MessageDigest对象
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// 4.获取明文字符串对应的字节数组
byte[] input = source.getBytes();
// 5.执行加密
byte[] output = messageDigest.digest(input);
// 6.创建BigInteger对象
int signum = 1;
BigInteger bigInteger = new BigInteger(signum, output);
// 7.按照16进制将bigInteger的值转换为字符串
int radix = 16;
String encoded = bigInteger.toString(radix).toUpperCase();
return encoded;
}
}
在 resources
目录下创建logback.xml
日记配置文件
<configuration debug="true">
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%npattern>
<charset>UTF-8charset>
encoder>
appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
root>
<logger name="com.atguigu" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT" />
logger>
configuration>
package com.atguigu.imperial.court.servlet.module;
import com.atguigu.imperial.court.servlet.base.ViewBaseServlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class PortalServlet extends ViewBaseServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 声明要访问的首页的逻辑视图
String templateName = "index";
// 调用父类的方法根据逻辑视图名称渲染视图
processTemplate(templateName, req, resp);
}
}
在 webapp
目录下 web.xml
文件添加:
<servlet>
<servlet-name>portalServletservlet-name>
<servlet-class>com.atguigu.imperial.court.servlet.module.PortalServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>portalServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
DOCTYPE html>
<html lang="en" xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>乾清宫title>
head>
<body>
<form th:action="@{/auth}" method="post">
<input type="hidden" name="method" value="login" />
<p style="color: red; font-weight: bold;" th:text="${message}">p>
<p style="color: red; font-weight: bold;" th:text="${systemMessage}">p>
账号:<input type="text" name="loginAccount"/><br/>
密码:<input type="password" name="loginPassword"><br/>
<button type="submit">进宫button>
form>
body>
html>
package com.atguigu.imperial.court.exception;
public class LoginFailedException extends RuntimeException {
public LoginFailedException() {
}
public LoginFailedException(String message) {
super(message);
}
public LoginFailedException(String message, Throwable cause) {
super(message, cause);
}
public LoginFailedException(Throwable cause) {
super(cause);
}
public LoginFailedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
public class ImperialCourtConst {
public static final String LOGIN_FAILED_MESSAGE = "账号、密码错误,不可进宫!";
public static final String ACCESS_DENIED_MESSAGE = "宫闱禁地,不得擅入!";
public static final String LOGIN_EMP_ATTR_NAME = "loginInfo";
}
package com.atguigu.imperial.court.servlet.module;
import com.atguigu.imperial.court.entity.Emp;
import com.atguigu.imperial.court.exception.LoginFailedException;
import com.atguigu.imperial.court.service.api.EmpService;
import com.atguigu.imperial.court.service.impl.EmpServiceImpl;
import com.atguigu.imperial.court.servlet.base.ModelBaseServlet;
import com.atguigu.imperial.court.util.ImperialCourtConst;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class AuthServlet extends ModelBaseServlet {
private EmpService empService = new EmpServiceImpl();
protected void login(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
try {
// 1、获取请求参数
String loginAccount = request.getParameter("loginAccount");
String loginPassword = request.getParameter("loginPassword");
// 2、调用 EmpService 方法执行登录逻辑
Emp emp = empService.getEmpByLoginAccount(loginAccount, loginPassword);
// 3、通过 request 获取 HttpSession 对象
HttpSession session = request.getSession();
// 4、将查询到的 Emp 对象存入 Session 域
session.setAttribute(ImperialCourtConst.LOGIN_EMP_ATTR_NAME, emp);
// 5、前往指定页面视图
String templateName = "temp";
processTemplate(templateName, request, response);
} catch (Exception e) {
e.printStackTrace();
// 6、判断此处捕获到的异常是否是登录失败异常
if (e instanceof LoginFailedException) {
// 7、如果是登录失败异常则跳转回登录页面
// ①将异常信息存入请求域
request.setAttribute("message", e.getMessage());
// ②处理视图:index
processTemplate("index", request, response);
}else {
// 8、如果不是登录异常则封装为运行时异常继续抛出
throw new RuntimeException(e);
}
}
}
}
<servlet>
<servlet-name>authServletservlet-name>
<servlet-class>com.atguigu.imperial.court.servlet.module.AuthServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>authServletservlet-name>
<url-pattern>/authurl-pattern>
servlet-mapping>
public class EmpServiceImpl implements EmpService {
private EmpDao empDao = new EmpDaoImpl();
@Override
public Emp getEmpByLoginAccount(String loginAccount, String loginPassword) {
// 1、对密码执行加密
String encodedLoginPassword = MD5Util.encode(loginPassword);
// 2、根据账户和加密密码查询数据库
Emp emp = empDao.selectEmpByLoginAccount(loginAccount, encodedLoginPassword);
// 3、检查 Emp 对象是否为 null
if (emp != null) {
// ①不为 null:返回 Emp
return emp;
} else {
// ②为 null:抛登录失败异常
throw new LoginFailedException(ImperialCourtConst.LOGIN_FAILED_MESSAGE);
}
}
}
public class EmpDaoImpl extends BaseDao<Emp> implements EmpDao {
@Override
public Emp selectEmpByLoginAccount(String loginAccount, String encodedLoginPassword) {
// 1、编写 SQL 语句
String sql = "select emp_id empId," +
"emp_name empName," +
"emp_position empPosition," +
"login_account loginAccount," +
"login_password loginPassword " +
"from t_emp where login_account=? and login_password=?";
// 2、调用父类方法查询单个对象
return super.getSingleBean(sql, Emp.class, loginAccount, encodedLoginPassword);
}
}
DOCTYPE html>
<html lang="en" xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>临时title>
head>
<body>
<p th:text="${session.loginInfo}">p>
body>
html>
登录成功:
<a th:href="@{/auth?method=logout}">退朝a>
protected void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、通过 request 对象获取 HttpSession 对象
HttpSession session = request.getSession();
// 2、将 HttpSession 对象强制失效
session.invalidate();
// 3、回到首页
String templateName = "index";
processTemplate(templateName, request, response);
}
刚开始是空的,还没有写方法:
public class WorkServlet extends ModelBaseServlet {
private MemorialsService memorialsService = new MemorialsServiceImpl();
}
<servlet>
<servlet-name>workServletservlet-name>
<servlet-class>com.atguigu.imperial.court.servlet.module.WorkServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>workServletservlet-name>
<url-pattern>/workurl-pattern>
servlet-mapping>
package com.atguigu.imperial.court.service.impl;
import com.atguigu.imperial.court.dao.api.MemorialsDao;
import com.atguigu.imperial.court.dao.impl.MemorialsDaoImpl;
import com.atguigu.imperial.court.service.api.MemorialsService;
public class MemorialsServiceImpl implements MemorialsService {
private MemorialsDao memorialsDao = new MemorialsDaoImpl();
}
public class WorkServlet extends ModelBaseServlet {
private MemorialsService memorialsService = new MemorialsServiceImpl();
protected void showMemorialsDigestList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、调用 Service 方法查询数据
List<Memorials> memorialsList = memorialsService.getAllMemorialsDigest();
// 2、将查询得到的数据存入请求域
request.setAttribute("memorialsList", memorialsList);
// 3、渲染视图
String templateName = "memorials-list";
processTemplate(templateName, request, response);
}
}
public class MemorialsServiceImpl implements MemorialsService {
private MemorialsDao memorialsDao = new MemorialsDaoImpl();
@Override
public List<Memorials> getAllMemorialsDigest() {
return memorialsDao.selectAllMemorialsDigest();
}
}
public class MemorialsDaoImpl extends BaseDao<Memorials> implements MemorialsDao {
@Override
public List<Memorials> selectAllMemorialsDigest() {
String sql = "select memorials_id memorialsId,\n" +
" memorials_title memorialsTitle,\n" +
// 取左边的10个字符,后面拼上...
" concat(left(memorials_content, 10), \"...\") memorialsContentDigest,\n" +
// t_emp表字段的 emp_name
" emp_name memorialsEmpName,\n" +
" memorials_create_time memorialsCreateTime,\n" +
" memorials_status memorialsStatus\n" +
" from t_memorials m left join t_emp e on m.memorials_emp=e.emp_id;";
return getBeanList(sql, Memorials.class);
}
}
<head>
<meta charset="UTF-8">
<title>奏折列表title>
<style type="text/css">
table {
border-collapse: collapse;
margin: 0px auto 0px auto;
}
table th, td {
border: 1px solid black;
text-align: center;
}
div {
text-align: right;
}
style>
head>
<body>
<div>
<span th:if="${session.loginInfo.empPosition == 'emperor'}">恭请皇上圣安span>
<span th:if="${session.loginInfo.empPosition == 'minister'}">给<span th:text="${session.loginInfo.empName}">XXXspan>大人请安span>
<a th:href="@{/auth?method=logout}">退朝a>
div>
body>
<body>
...
<table>
<thead>
<tr>
<th>奏折标题th>
<th>内容摘要th>
<th>上疏大臣th>
<th>上疏时间th>
<th>奏折状态th>
<th>奏折详情th>
tr>
thead>
<tbody th:if="${#lists.isEmpty(memorialsList)}">
<tr>
<td colspan="6">没有人上过折子td>
tr>
tbody>
<tbody th:if="${not #lists.isEmpty(memorialsList)}">
<tr th:each="memorials : ${memorialsList}">
<td th:switch="${memorials.memorialsStatus}">
<span th:text="${memorials.memorialsTitle}" th:case="0" style="color: red;">奏折标题span>
<span th:text="${memorials.memorialsTitle}" th:case="1" style="color: blue;">奏折标题span>
<span th:text="${memorials.memorialsTitle}" th:case="2">奏折标题span>
td>
<td th:switch="${memorials.memorialsStatus}">
<span th:text="${memorials.memorialsContentDigest}" th:case="0" style="color: red;">内容摘要span>
<span th:text="${memorials.memorialsContentDigest}" th:case="1" style="color: blue;">内容摘要span>
<span th:text="${memorials.memorialsContentDigest}" th:case="2">内容摘要span>
td>
<td th:switch="${memorials.memorialsStatus}">
<span th:text="${memorials.memorialsEmpName}" th:case="0" style="color: red;">上疏大臣span>
<span th:text="${memorials.memorialsEmpName}" th:case="1" style="color: blue;">上疏大臣span>
<span th:text="${memorials.memorialsEmpName}" th:case="2">上疏大臣span>
td>
<td th:switch="${memorials.memorialsStatus}">
<span th:text="${memorials.memorialsCreateTime}" th:case="0" style="color: red;">上疏时间span>
<span th:text="${memorials.memorialsCreateTime}" th:case="1" style="color: blue;">上疏时间span>
<span th:text="${memorials.memorialsCreateTime}" th:case="2">上疏时间span>
td>
<td th:switch="${memorials.memorialsStatus}">
<span th:case="0" style="color: red;">未读span>
<span th:case="1" style="color: blue;">已读span>
<span th:case="2">已批示span>
td>
<td>
<a th:href="@{/work?method=detail}">奏折详情a>
td>
tr>
tbody>
table>
body>
测试:
先登录成后,在地址栏输入http://localhost:8080/demo/work?method=showMemorialsDigestList
修改AuthServlet
类的login()
方法
// 5、前往指定页面视图
// 前往临时页面
// String templateName = "temp";
// processTemplate(templateName, request, response);
// 前往正式的目标地址
response.sendRedirect(request.getContextPath() + "/work?method=showMemorialsDigestList");
测试:
在/webapp/WEB-INFO/pages/
目录下memorials-list.html
文件
<a th:href="@{/work(method='showMemorialsDetail',memorialsId=${memorials.memorialsId})}">奏折详情a>
在WorkServlet
类添加showMemorialsDetail()
方法
protected void showMemorialsDetail(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// 1、从请求参数读取 memorialsId
String memorialsId = request.getParameter("memorialsId");
// 2、根据 memorialsId 从 Service 中查询 Memorials 对象
Memorials memorials = memorialsService.getMemorialsDetailById(memorialsId);
// 3、将 Memorials 对象存入请求域
request.setAttribute("memorials", memorials);
// 4、解析渲染视图
String templateName = "memorials_detail";
processTemplate(templateName, request, response);
}
public class MemorialsServiceImpl implements MemorialsService {
private MemorialsDao memorialsDao = new MemorialsDaoImpl();
@Override
public Memorials getMemorialsDetailById(String memorialsId) {
return memorialsDao.selectMemorialsById(memorialsId);
}
}
public class MemorialsDaoImpl extends BaseDao<Memorials> implements MemorialsDao {
@Override
public Memorials selectMemorialsById(String memorialsId) {
String sql = "select memorials_id memorialsId,\n" +
" memorials_title memorialsTitle,\n" +
" memorials_content memorialsContent,\n" +
" emp_name memorialsEmpName,\n" +
" memorials_create_time memorialsCreateTime,\n" +
" memorials_status memorialsStatus,\n" +
" feedback_time feedbackTime,\n" +
" feedback_content feedbackContent\n" +
"from t_memorials m left join t_emp e on m.memorials_emp=e.emp_id " +
"where memorials_id=?;";
return getSingleBean(sql, Memorials.class, memorialsId);
}
}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>奏折详情title>
<style type="text/css">
table {
border-collapse: collapse;
margin: 0px auto 0px auto;
width: 70%;
}
table th, td {
border: 1px solid black;
text-align: center;
}
div {
text-align: right;
}
style>
head>
<body>
<div>
<span th:if="${session.loginInfo.empPosition == 'emperor'}">恭请皇上圣安span>
<span th:if="${session.loginInfo.empPosition == 'minister'}">给<span th:text="${session.loginInfo.empName}">XXXspan>大人请安span>
<a th:href="@{/auth?method=logout}">退朝a>
div>
<table>
<tr>
<td>奏折标题td>
<td th:text="${memorials.memorialsTitle}">td>
tr>
<tr>
<td>上疏大臣td>
<td th:text="${memorials.memorialsEmpName}">td>
tr>
<tr>
<td>上疏时间td>
<td th:text="${memorials.memorialsCreateTime}">td>
tr>
<tr>
<td>奏折内容td>
<td th:text="${memorials.memorialsContent}">td>
tr>
<tr th:if="${memorials.memorialsStatus == 2}">
<td>批复时间td>
<td th:text="${memorials.feedbackTime}">td>
tr>
<tr th:if="${memorials.memorialsStatus == 2}">
<td>批复内容td>
<td th:text="${memorials.feedbackContent}">td>
tr>
table>
<div th:if="${memorials.memorialsStatus != 2}">
<form th:action="@{/work}" method="post">
<input type="hidden" name="method" value="feedBack" />
<input type="hidden" name="memorialsId" th:value="${memorials.memorialsId}"/>
<textarea name="feedbackContent">textarea>
<button type="submit">御批button>
form>
div>
<a th:href="@{/work?method=showMemorialsDigestList}">返回列表a>
body>
html>
一份未读奏折,点击查看后,需要从未读变成已读。
在WorkServlet
类修改showMemorialsDetail()
方法,增加判断:
protected void showMemorialsDetail(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// 1、从请求参数读取 memorialsId
String memorialsId = request.getParameter("memorialsId");
// 2、根据 memorialsId 从 Service 中查询 Memorials 对象
Memorials memorials = memorialsService.getMemorialsDetailById(memorialsId);
// **********************补充功能**********************
// 获取当前奏折对象的状态
Integer memorialsStatus = memorials.getMemorialsStatus();
// 判断奏折状态
if (memorialsStatus == 0) {
// 更新奏折状态:数据库修改
memorialsService.updateMemorialsStatusToRead(memorialsId);
// 更新奏折状态:当前对象修改
memorials.setMemorialsStatus(1);
}
// **********************补充功能**********************
// 3、将 Memorials 对象存入请求域
request.setAttribute("memorials", memorials);
// 4、解析渲染视图
String templateName = "memorials_detail";
processTemplate(templateName, request, response);
}
public class MemorialsServiceImpl implements MemorialsService {
private MemorialsDao memorialsDao = new MemorialsDaoImpl();
@Override
public void updateMemorialsStatusToRead(String memorialsId) {
memorialsDao.updateMemorialsStatusToRead(memorialsId);
}
}
public class MemorialsDaoImpl extends BaseDao<Memorials> implements MemorialsDao {
@Override
public void updateMemorialsStatusToRead(String memorialsId) {
String sql = "update t_memorials set memorials_status=1 where memorials_id=?";
update(sql, memorialsId);
}
}
上面的御批表格可以改下:在memorials_detail.html
修改一下
<div style="margin: 0 auto 0 auto;width: 60%;" th:if="${memorials.memorialsStatus != 2}">
<form th:action="@{/work}" method="post">
<input type="hidden" name="method" value="feedBack" />
<input type="hidden" name="memorialsId" th:value="${memorials.memorialsId}"/>
<textarea name="feedbackContent" style="width: 500px;height: 200px">textarea>
<button type="submit">御批button>
form>
div>
提交表单,更新数据。
在WorkServlet
类添加feedBack()
方法
protected void feedBack(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取表单提交的请求参数
String memorialsId = request.getParameter("memorialsId");
String feedbackContent = request.getParameter("feedbackContent");
// 执行更新
memorialsService.updateMemorialsFeedBack(memorialsId, feedbackContent);
// 重定向回显示奏折列表页面
response.sendRedirect(request.getContextPath() + "/work?method=showMemorialsDigestList");
}
public class MemorialsServiceImpl implements MemorialsService {
private MemorialsDao memorialsDao = new MemorialsDaoImpl();
@Override
public void updateMemorialsFeedBack(String memorialsId, String feedbackContent) {
memorialsDao.updateMemorialsFeedBack(memorialsId, feedbackContent);
}
}
public class MemorialsDaoImpl extends BaseDao<Memorials> implements MemorialsDao {
@Override
public void updateMemorialsFeedBack(String memorialsId, String feedbackContent) {
String feedbackTime = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
String sql = "update t_memorials set memorials_status=2,feedback_content=?,feedback_time=? where memorials_id=?";
update(sql, feedbackContent, feedbackTime, memorialsId);
}
}
package com.atguigu.imperial.court.filter;
import com.atguigu.imperial.court.util.ImperialCourtConst;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 1、获取 HttpSession 对象
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpSession session = request.getSession();
// 2、尝试从 Session 域获取已登录的对象
Object loginEmp = session.getAttribute(ImperialCourtConst.LOGIN_EMP_ATTR_NAME);
// 3、判断 loginEmp 是否为空
if (loginEmp != null) {
// 4、若不为空则说明当前请求已登录,直接放行
filterChain.doFilter(request, servletResponse);
return ;
}
// 5、若为空说明尚未登录,则回到登录页面
request.setAttribute("systemMessage", ImperialCourtConst.ACCESS_DENIED_MESSAGE);
request.getRequestDispatcher("/").forward(request, servletResponse);
}
@Override
public void destroy() {}
}
把 LoginFilter
放在 TransactionFilter
前面声明,原因是:如果登录检查失败不放行,直接跳转到页面,此时将不必执行 TransactionFilter
中的事务操作,可以节约性能。
<filter>
<filter-name>loginFilterfilter-name>
<filter-class>com.atguigu.imperial.court.filter.LoginFilterfilter-class>
filter>
<filter-mapping>
<filter-name>loginFilterfilter-name>
<url-pattern>/workurl-pattern>
filter-mapping>
测试:
请求http://localhost:8080/demo/work?method=showMemorialsDetail&memorialsId=5
MySQL 连接信息中,IP 地址部分需要改成 localhost。
mvn clean package -Dmaven.test.skip=true
可以人为指定最终 war 包名称:
<build>
<finalName>demo-mefinalName>
build>
上传到tomcat的webapps
目录下
/opt/apache-tomcat-8.5.75/bin/startup.sh