1.Spring总体技术体系
框架 = jar包+配置文件
2.框架的优点
1.提高开发效率:框架提供了许多预先设计好了的组件和工具,能够帮助开发人员快速进行开发。相较于传统手写代码,在框架提供的规范化环境中,开发者可以更快地实现项目的各种要求。
2.降低开发成本:框架的提供标准化的编程语言、数据操作等代码片段,避免了重复开发的问题,降低了开发成本,提供深度优化的系统,降低了维护成本,增强了系统的可靠性。
3.提高应用程序的稳定性:框架通常经过了很长时间的开发和测试,其中的许多组件、代码片段和设计模式都得到了验证。重复利用这些组件有助于减少bug的出现,从而提高了应用程序的稳定性。
4.提供标准化的解决方案:框架通常是针对某个特定领域的,通过提供标准化的解决方案,可以为开发人员提供一种共同的语言和思想基础,有助于更好地沟通和协作。
3.框架的缺点
1.学习成本高:框架通常具有特定的语言和编程范式。对于开发人员而言,需要花费时间学习其背后的架构、模式和逻辑,这对于新手而言可能会耗费较长时间。
2.可能存在局限性:虽然框架提高了开发效率并可以帮助开发人员解决常见问题,但是在某些情况下,特定的应用需求可能超出框架的范围,从而导致应用程序无法满足要求。开发人员可能需要更多的控制权和自由度,同时需要在框架和应用程序之间进行权衡取舍。
3.版本变更和兼容性问题:框架的版本发布和迭代通常会导致代码库的大规模变更,进而导致应用程序出现兼容性问题和漏洞。当框架变更时,需要考虑框架是否向下兼容,以及如何进行适当的测试、迁移和升级。
4.架构风险:框架涉及到很多抽象和概念,如果开发者没有足够的理解和掌握其架构,可能会导致系统出现设计和架构缺陷,从而影响系统的健康性和安全性。
4.组件
常规的三层架构处理请求流程:
整个项目就是由各种组件搭建而成的:
5.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.csi</groupId>
<artifactId>sss</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-framework>5.3.37</spring-framework>
</properties>
<dependencies>
<!--spring context依赖-->
<!--当你引入SpringContext依赖之后,表示将Spring的基础依赖引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework}</version>
</dependency>
<!-- 数据库驱动和连接池-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.53</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-framework}</version>
</dependency>
<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.8.0-beta4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.8.0-beta4</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>4.0.4</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring-framework}</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
</dependencies>
</project>
6.web.xml配置
在web.xml中,添加configLocation属性,能够找到Spring的核心配置文件
<?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_4_0.xsd"
version="4.0">
<!--获取核心配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--配置Spring监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
7.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 导入外部属性文件 -->
<context:property-placeholder location="classpath*:jdbc.properties" />
<!-- 配置Druid数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="initialSize" value="10"/>
<property name="maxActive" value="20"/>
</bean>
<!-- 配置 JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 装配数据源 -->
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置dao与jabcTemplate对象的关系-->
<bean id="userDao" class="com.csi.dao.impl.UserDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="userService" class="com.csi.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
</beans>
7.1Spring Ioc/DI实现步骤
7.1.1配置元数据
基于 XML 的配置元数据的基本结构:
<bean id="..." [1] class="..." [2]>
<!-- collaborators and configuration for this bean go here -->
</bean>
Spring IoC 容器管理一个或多个组件。这些 组件是使用你提供给容器的配置元数据(例如,以 XML <bean/>
定义的形式)创建的。
<bean /> 标签 == 组件信息声明
1.id
属性是标识单个 Bean 定义的字符串。
2.class
属性定义 Bean 的类型并使用完全限定的类名
7.12.实例化Ioc容器
提供给 ApplicationContext
构造函数的位置路径是资源字符串地址,允许容器从各种外部资源(如本地文件系统、Java CLASSPATH
等)加载配置元数据。
//实例化ioc容器,读取外部配置文件,最终会在容器内进行ioc和di动作
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
7.1 3.获取Bean(组件)
ApplicationContext
是一个高级工厂的接口,能够维护不同 bean 及其依赖项的注册表。通过使用方法 T getBean(String name, Class<T> requiredType)
,可以检索 bean 的实例。 允许读取 Bean 定义并访问它们
//创建ioc容器对象,指定配置文件,ioc也开始实例组件对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取ioc容器的组件对象
UserDao userDao = (UserDao) context.getBean("userDao");
//使用组件对象
User user = userDao.login("admin", "1");
7.2基于XML配置方式组件管理
7.2.1基于无参构造函数
当通过构造函数方法创建一个 bean(组件对象) 时,所有普通类都可以由 Spring 使用并与之兼容。也就是说,正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。只需指定 Bean 类信息就足够了。但是,默认情况下,我们需要一个默认(空)构造函数。
@Getter
@Setter
public class User {
//默认包含无参数构造函数
public void show() {
System.out.println("我有默认无参构造函数");
}
}
创建携带spring约束的xml配置文件:resources/spring-bean-01.xml
<bean id="userDao" class="com.csi.dao.impl.UserDaoImpl"/>
-
bean标签:通过配置bean标签告诉IoC容器需要创建对象的组件信息
-
id属性:bean的唯一标识,方便后期获取Bean!
-
class属性:组件类的全限定符!
-
注意:要求当前组件类必须包含无参数构造函数!
7.2.2基于静态工厂方法实例化
除了使用构造函数实例化对象,还有一类是通过工厂模式实例化对象,以其他例子为例:
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
...
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
-
class属性:指定工厂类的全限定符!
-
factory-method: 指定静态工厂方法,注意,该方法必须是static方法。
7.2.3基于实例工厂方法实例化
使用实例工厂方法创建Bean的配置
public class DefaultServiceLocator {
private ClientServiceImpl clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
...
<!-- 将工厂类进行ioc配置 -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
</bean>
<!-- 根据工厂对象的实例工厂方法进行实例化组件对象 -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
-
factory-bean属性:指定当前容器中工厂Bean 的名称。
-
factory-method: 指定实例工厂方法名。注意,实例方法必须是非static的
7.3组件(Bean)依赖注入
通过配置文件,实现IoC容器中Bean之间的引用(依赖注入DI配置)。
7.3.1基于构造函数的依赖注入(单个构造函数)
基于构造函数的 DI 是通过容器调用具有多个参数的构造函数来完成的,每个参数表示一个依赖项。
组件类
public class UserDao {
}
public class UserService {
private UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
配置文件
<beans>
<!-- 引用类bean声明 -->
<bean id="userService" class="com.csi.service.impl.UserServiceImpl">
<!-- 构造函数引用 -->
<constructor-arg ref="userDao"/>
<constructor-arg index="索引位置" name="参数名称" type="参数的类型" value="" ref=""/>
</bean>
<!-- 被引用类bean声明 -->
<bean id="userDao" class="UserDao"/>
</beans>
7.3.2基于构造函数的依赖注入(多构造参数)
public class UserDao {
}
public class UserService {
private UserDao userDao;
private int age;
private String name;
public UserService(int age , String name ,UserDao userDao) {
this.userDao = userDao;
this.age = age;
this.name = name;
}
}
配置文件
1.多参数,可以按照相应构造函数的顺序注入数据
<beans>
<bean id="userService" class="com.csi.service.impl.UserServiceImpl">
<!-- value直接注入基本类型值 -->
<constructor-arg value="18"/>
<constructor-arg value="admin"/>
<constructor-arg ref="userDao"/>
</bean>
<!-- 被引用类bean声明 -->
<bean id="userDao" class="com.csi.dao.impl.UserDaoImpl"/>
</beans>
2.多参数,可以按照相应构造函数的名称注入数据
<beans>
<bean id="userService" class="com.csi.service.impl.UserServiceImpl">
<!-- value直接注入基本类型值 -->
<constructor-arg name="name" value="admin"/>
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="age" value="18"/>
</bean>
<!-- 被引用类bean声明 -->
<bean id="userDao" class="com.csi.dao.impl.UserDaoImpl"/>
</beans>
3. 多参数,可以按照相应构造函数的角标注入数据 index从0开始 构造函数(0,1,2....)
<beans>
<bean id="userService" class="com.csi.service.impl.UserServiceImpl">
<!-- value直接注入基本类型值 -->
<constructor-arg index="1" value="admin"/>
<constructor-arg index="2" ref="userDao"/>
<constructor-arg index="0" value="18"/>
</bean>
<!-- 被引用类bean声明 -->
<bean id="userDao" class="com.csi.dao.impl.UserDaoImpl"/>
</beans>
7.3.3基于Setter方法依赖注入
public class UserDaoImpl implements UserDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public User login(String userCode, String password) {
RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);
String sql = "select * from smbms_user where userCode=? and userPassword=?";
User user = jdbcTemplate.queryForObject(sql,mapper, userCode, password);
return user;
}
}
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Override
public User login(String userCode, String password) {
return userDao.login(userCode, password);
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
配置文件
<bean id="userDao" class="com.csi.dao.impl.UserDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/> <!-- 注入 JdbcTemplate -->
</bean>
<!-- setter方法,注入基本数据类型userDao
name = 属性名 value= 基本类型值
-->
<bean id="userService" class="com.csi.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
-
property标签: 可以给setter方法对应的属性赋值
-
property 标签: name属性代表set方法标识、ref代表引用bean的标识id、value属性代表基本属性值
8.jdbc.properties文件
jdbc.url=jdbc:mysql://localhost:3306/smbms
jdbc.driver=com.mysql.cj.jdbc.Driver
//数据库连接账户名
jdbc.username=root
//数据库连接密码
jdbc.password=102815
9.BaseController
public class BaseController extends HttpServlet {
private WebApplicationContext webCtx ;
@Override
public void init() throws ServletException {
webCtx=WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()) ;
}
public Object getBean(String id) {
return webCtx.getBean(id) ;
}
}
BaseController
继承自 HttpServlet
,这意味着它是一个 Servlet,可以处理 HTTP 请求。
WebApplicationContext
的使用:
WebApplicationContext
是 Spring 的一个上下文接口,它包含了 Spring 应用程序的所有信息,能够管理 Spring bean 的生命周期和依赖注入。
在 init()
方法中,使用 WebApplicationContextUtils.getWebApplicationContext(this.getServletContext())
获取当前 Servlet 上下文中的 WebApplicationContext
实例。这样,可以在 Servlet 中使用 Spring 管理的 bean。
getBean
方法:
getBean(String id)
方法允许通过 bean 的 ID 从 Spring 容器中获取对应的 bean 实例。这使得在 Servlet 中能够方便地访问 Spring 管理的服务和组件。
注:使用tomcat10版本,存在版本的兼容性问题,介意使用tomcat9的版本
一.登录
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>系统登录 - 超市账单管理系统</title>
<link type="text/css" rel="stylesheet" href="css/style.css"/>
<script type="text/javascript" src="JQuery/jquery-1.11.3.min.js"></script>
<style type="text/css">
#login .hint {
/* border: 1px solid red; */
width: 120px;
top: 120px;
margin-left: 235px;
margin-top: -70px;
color: red;
}
</style>
<%
String type = (String) request.getAttribute("type");
%>
</head>
<body class="blue-style">
<div id="login">
<div class="icon"></div>
<div class="login-box">
<form id="loginForm">
<dl>
<dt>用户名:</dt>
<dd><input type="text" name="userCode" class="input-text" id="userCode"/></dd>
<dt>密 码:</dt>
<dd><input type="password" name="passWord" class="input-text" id="password"/></dd>
</dl>
<div class="buttons">
<input type="button" name="submit" value="登录系统" class="input-button" id="submitLogin"/>
<input type="reset" name="reset" value="重 填" class="input-button"/>
</div>
</form>
</div>
</div>
</body>
</html>
在登录时,先传入用户账号和密码,通过dao层向数据库中查找该用户信息,如果用户存在则返回用户信息并跳转到系统首页,如果用户不存在则提示无法登录。
登录页面AJAX请求
<script type="text/javascript">
$(function () {
$("#submitLogin").click(function (event) {
event.preventDefault(); // 阻止默认提交
var subUserCode = $("#userCode").val();//获取输入的登录账户
var password = $("#password").val();//获取输入的登录密码
$.post("http://localhost:8080/smbms/LoginController",//发起AJAX请求
{userCode: subUserCode, pass: password},
function (loginResult) {
if (loginResult.code === 200) {
// 登录成功,重定向到主页面
location.href = "http://localhost:8080/smbms/admin_index.html";
} else {
// 登录失败,显示错误提示
alert("账号或密码错误!")
location.href = "http://localhost:8080/smbms/login.jsp";
}
},
"json" // 指定返回的数据类型为 JSON
)
});
});
</script>
User类
@Getter
@Setter
public class User {
private long id;
private String userCode;
private String userName;
private String userPassword;
private int gender;
private Date birthday;
private String phone;
private String address;
private long userRole;
private Long createBy;
private Date creationDate;
private Long modifyBy;
private Date modifyDate;
public User(String userCode, String userPassword) {
this.userCode = userCode;
this.userPassword = userPassword;
}
public User() {
}
}
UserDaoImpl代码
public class UserDaoImpl implements UserDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public User login(String userCode, String password) {
RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);
String sql = "select * from smbms_user where userCode=? and userPassword=?";
User user = jdbcTemplate.queryForObject(sql,mapper, userCode, password);
return user;
}
}
UserServiceImpl代码
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Override
public User login(String userCode, String password) {
return userDao.login(userCode, password);
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
UserDaoImpl测试类1
public class LoginTest {
@Test
public void testLogin() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);
String sql = "select * from smbms_user where userCode=? and userPassword=?";
User user = jdbcTemplate.queryForObject(sql, mapper, "admin", "1");
System.out.println(user);
}
}
问题分析:出现了 org.springframework.beans.TypeMismatchException
异常,提示无法将 null
值转换为基本类型 long
原因,在创建User对象时,user对象拥有的字段中有包含数据类型为long型的字段,而数据库中该字段的内容为null,在 Java 中,基本类型(如 long
, int
等)不能接受 null
值,而包装类型(如 Long
, Integer
等)可以。因此,如果 User
类中的 某一属性是 long
类型,而数据库查询结果中该字段的值为 null
,就会导致转换失败。
解决方法:将user对象中的long类型改为Long包装类型。
UserDaoImpl测试类2
@Test
public void testLogin() {
UserDao userDao = new UserDaoImpl();
User user = userDao.login("admin", "1");
System.out.println(user);
}
报错点:提示无法调用 org.springframework.jdbc.core.JdbcTemplate.queryForObject
,因为 this.jdbcTemplate
为 null
。这个问题的根本原因是 JdbcTemplate
实例未被正确注入到 UserDaoImpl
中
问题分析:
-
未注入 JdbcTemplate:在
UserDaoImpl
类中,jdbcTemplate
变量是一个依赖项,它需要通过 Spring 容器进行注入。如果在创建UserDaoImpl
实例时没有设置JdbcTemplate
,则会导致jdbcTemplate
为null
,从而在调用queryForObject
方法时抛出NullPointerException
。 -
测试方法中的实例化:在
testLogin
方法中,直接通过new UserDaoImpl()
创建了UserDaoImpl
的实例,但没有调用setJdbcTemplate
方法来注入JdbcTemplate
实例。
解决方法:在测试方法中,需要确保 UserDaoImpl
是通过 Spring 容器获取的,而不是直接通过 new
关键字创建的
UserDaoImpl测试类2正确测试代码:
@Test
public void testLogin() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) context.getBean("userDao"); // 从 Spring 容器获取 UserDao 实例
User user = userDao.login("admin", "1");
System.out.println(user);
}
LoginController代码
继承BaseController,直接在代码中通过 id 从 Spring 容器中获取 bean
@WebServlet("/LoginController")
public class LoginController extends BaseController{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf-8");
String userCode = req.getParameter("userCode");
String pass = req.getParameter("pass");
UserService userService = (UserService) getBean("userService");
User user = userService.login(userCode, pass);
Result<User> loginResult=null;
if(user != null){
HttpSession session = req.getSession();
session.setAttribute("userinfo", user);
System.out.println(user);
loginResult=new Result<>("登录成功",200);
loginResult.setData(user);
}else if (user==null){
loginResult=new Result<>("登录失败",300);
}
String json = JSON.toJSONString(loginResult);
PrintWriter out = resp.getWriter();
out.print(json);
out.close();
}
}
登录成功则跳转页面
如果用户密码与账号不匹配,则不进行跳转,直接返回登录页面,在测试时,遇到以下错误,这是因为如果没有找到相关用户的记录,查询将返回 0 行,从而导致 EmptyResultDataAccessException
异常。
如果希望在没有找到用户时不抛出异常,可以在 login
方法中使用 try-catch 块捕获 EmptyResultDataAccessException
修改dao层的代码如下
@Override
public User login(String userCode, String password) {
RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);
String sql = "select * from smbms_user where userCode=? and userPassword=?";
User user =null;
try {
user = jdbcTemplate.queryForObject(sql,mapper, userCode, password);
return user;
}catch (EmptyResultDataAccessException e) {
return null;
}
}
首页头部
在进行登录时,设置了session,将登录查找到的用户信息通过session.setAttribute("userinfo", user);存入session中,在首页直接通过request.getSession().getAttribute("userinfo")获取到用户信息,并显示在头部
admin_top.jsp
<%@ page import="com.csi.domain.User" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<link type="text/css" rel="stylesheet" href="css/style.css" />
<script type="text/javascript" src="JQuery/jquery-1.11.3.min.js"></script>
<%
User userinfo = (User) request.getSession().getAttribute("userinfo");
%>
</head>
<body>
<div id="header">
<div class="title"></div>
<div class="welcome">欢迎您:<%=userinfo.getUserName()%></div>
<div class="welcome"></div>
</div>
</body>
</html>
二.账单管理
admin_bill_list.jsp
<%@ page import="com.csi.domain.Provider" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="com.csi.domain.Order" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
<link type="text/css" rel="stylesheet" href="css/style.css"/>
<style>
<%-- 分页样式--%>
.content .list a, .content .list a:visited {
text-decoration: none;
color: white;
width: 15px;
height: 10px;
background-color: #92d1f4;
/*margin-right: 2px;*/
border-radius: 2px;
}
.content .list {
display: inline-block;
vertical-align: middle;
}
.page-wrap {
/*border: 1px solid black;*/
margin-top: 100px;
margin-left: 550px;
}
.page {
text-decoration: none;
border: 1px solid #92d1f4;
padding: 2px 4px;
margin-right: 2px;
margin-left: 2px;
color: black;
}
</style>
<%
List<Order> orders = (List<Order>) session.getAttribute("orderList");
int totalCount = (int) session.getAttribute("totalCount"); //总条数
int pageSize = (int) session.getAttribute("pageSize"); //每页显示条数
int totalPageNo = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1; //一共多少页
int pageNum = (int) session.getAttribute("pageNum"); //当前页
int reviewPageNO = pageNum <= 1 ? reviewPageNO = 1 : pageNum - 1; //计算上一页的页码
int nextPageNo = pageNum >= totalPageNo ? nextPageNo = totalPageNo : pageNum + 1; //计算下一页页码
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
%>
</head>
<body>
<div class="menu">
<form method="get" action="<%=request.getContextPath()%>/FindOrderByCondition">
商品名称:<input type="text" name="productName" class="input-text"/>
供应商名称:<select name="providerId">
<option value="-1">请选择</option>
<option value="></option>
</select>
是否付款:<select name="payStatus">
<option value="-1">请选择</option>
<option value="1">已付款</option>
<option value="2">未付款</option>
</select>
<input type="submit" name="submit" value="组合查询" class="button"/>
</form>
</div>
<div class="main">
<div class="optitle clearfix">
<em><input type="button" name="button" value="添加数据" class="input-button" style="margin-left: 20px"
onclick="location.href='orderAdd.jsp'"/></em>
<em><input type="button" name="button" value="批量删除" class="input-button" onclick="deleteMore()"/></em>
<div class="title">账单管理>></div>
</div>
<div class="content">
<table class="list">
<tr>
<td style="width: 60px">
<form id="deleteAll" action="<%=request.getContextPath()%>/DeleteOrderController" method="get"
style="display: inline-block">
<input type="checkbox" id="allCheck" name="allCheck" onclick="choiceAll()" value="on"/>全选
</form>
</td>
<td>账单编号</td>
<td style="width: 60px">商品名称</td>
<td style="width: 60px">商品数量</td>
<td style="width: 60px">交易金额</td>
<td style="width: 60px">是否付款</td>
<td style="width: 120px;">供应商名称</td>
<td style="width: 100px;">商品描述</td>
<td>账单时间</td>
<td style="width: 120px;">操 作</td>
</tr>
<form id="deleteForm" action="<%=request.getContextPath()%>/DeleteOrderController" method="post">
<div>
<%
if (orders == null || orders.size() == 0) {
String tishi = "当前没有该账单信息!";
%>
<%=tishi%>
<%
} else {
for (Order order : orders) {
%>
<tr>
<td><input id="checkOn" name="checkOn" type="checkbox" value="<%=order.getId()%>"
style="display: inline-block;vertical-align: middle"/></td>
<td><%=order.getBillCode()%>
</td>
<td><%=order.getProductName()%>
</td>
<td><%=order.getProductCount()%>
</td>
<td><%=order.getTotalPrice()%>
</td>
<td>
<%
if (order.getIsPayment() == 1) {
%>
<span>未支付</span>
<%
} else if (order.getIsPayment() == 2) {
%>
<span>已支付</span>
<%
}
%>
</td>
<td><%=order.getProvider().getProName()%>
</td>
<td><%=order.getProductDesc()%>
</td>
<td>
<%
String time = sdf.format(order.getCreationDate());
%>
<%=time%>
</td>
<td>
<a class="caozuo"
href="<%=request.getContextPath()%>/orderModify.jsp?billCode=<%=order.getBillCode()%>&productName=<%=order.getProductName()%>">修改</a>
<a class="caozuo" onclick="return confirm('确定删除吗?')"
href="<%=request.getContextPath()%>//DeleteController?billCode=<%=order.getBillCode()%>">删除</a>
<a class="caozuo" href="#">详情</a>
</td>
</tr>
<%
}
}
%>
</div>
</form>
</table>
</div>
<div class="page-wrap">
<a href="<%=request.getContextPath()%>/OrderListController?pageNum=<%=reviewPageNO%>&pageSize=<%=pageSize%>"
class="pagePre page">上一页</a>
<%
for (int i = 1; i <= totalPageNo; i++) {
%>
<a href="<%=request.getContextPath()%>/OrderListController?pageNum=<%=i%>&pageSize=<%=pageSize%>"
class="page pageNo"><%=i%>
</a>
<%
}
%>
<a href="<%=request.getContextPath()%>/OrderListController?pageNum=<%=nextPageNo%>&pageSize=<%=pageSize%>"
class="pageNext page">下一页</a>
</div>
</div>
<script type="text/javascript">
function deleteMore() {
if (confirm("确定要删除选中的订单吗?")) {
document.getElementById("deleteForm").submit();
}
}
</script>
</body>
</html>
2.1 账单管理-账单信息显示
在applicationContext.xml中加入dao与jabcTemplate对象的关系
<bean id="orderDao" class="com.csi.dao.impl.OrderDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="orderService" class="com.csi.service.impl.OrderServiceImpl">
<property name="orderDao" ref="orderDao"/>
</bean>
Order类
@Setter
@Getter
public class Order {
private long id;
private String billCode;
private String productName;
private String productDesc;
private String productUnit;
private double productCount;
private double totalPrice;
private int isPayment;
private Long createdBy;
private Date creationDate;
private Long modifyBy;
private Date modifyDate;
private long providerId;
private Provider provider ;
}
OrderRowMapper类
public class OrderRowMapper implements RowMapper<Order> {
@Override
public Order mapRow(ResultSet rs, int rowNum) throws SQLException {
Order order = new Order();
order.setId(rs.getLong("id"));
order.setBillCode(rs.getString("billCode"));
order.setProductName(rs.getString("productName"));
order.setProductDesc(rs.getString("productDesc"));
order.setProductUnit(rs.getString("productUnit"));
order.setProductCount(rs.getDouble("productCount"));
order.setTotalPrice(rs.getDouble("totalPrice"));
order.setIsPayment(rs.getInt("isPayment"));
order.setCreatedBy(rs.getLong("createdBy"));
order.setCreationDate(rs.getDate("creationDate"));
order.setModifyBy(rs.getLong("modifyBy"));
order.setModifyDate(rs.getDate("modifyDate"));
order.setProviderId(rs.getLong("providerId"));
// 初始化 Provider 对象,在查找order数据时,provider字段需要什么信息则保留什么信息
Provider provider = new Provider();
// provider.setId(rs.getLong("p.id")); // 确保列名正确
// provider.setProCode(rs.getString("p.proCode"));
provider.setProName(rs.getString("p.proName"));
provider.setProDesc(rs.getString("p.proDesc"));
// provider.setProContact(rs.getString("p.proContact"));
// provider.setProPhone(rs.getString("p.proPhone"));
// provider.setProAddress(rs.getString("p.proAddress"));
// provider.setProFax(rs.getString("p.proFax"));
// provider.setCreatedBy(rs.getLong("p.createdBy"));
// provider.setCreationDate(rs.getDate("p.creationDate"));
// provider.setModifyBy(rs.getLong("p.modifyBy"));
// provider.setModifyDate(rs.getDate("p.modifyDate"));
order.setProvider(provider); // 将 Provider 设置到 Order 中
return order;
}
}
OrderDaoImpl代码
public class OrderDaoImpl implements OrderDao {
private JdbcTemplate jdbcTemplate;
@Override
public List<Order> list() {
String sql = "SELECT b.*, p.* FROM smbms_bill b JOIN smbms_provider p ON b.providerId = p.id LIMIT ? OFFSET ?";
int offset = (pageNum - 1) * pageSize;//表示从哪一条记录开始返回数据
//new Object[]{pageSize, offset},对象数组,包含 SQL 查询中需要绑定的参数
// pageSize:表示每页显示的记录数。
return jdbcTemplate.query(sql, new Object[]{pageSize, offset}, new OrderRowMapper());
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
OrderServicImpl代码
public class OrderServiceImpl implements OrderService {
private OrderDaoImpl orderDao;
@Override
public List<Order> list() {
return orderDao.list();
}
public void setOrderDao(OrderDaoImpl orderDao) {
this.orderDao = orderDao;
}
}
OrderDaoImpl-list测试类
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderDao orderDao = (OrderDao) context.getBean("orderDao");
List<Order> orderList = orderDao.list();
System.out.println(orderList);
}
@Test public void test1() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderService orderService= (OrderService) context.getBean("orderService");
List<Order> orderList = orderService.list();
System.out.println(orderList);
}
报错点:这个错误提示表明尝试访问
Provider
对象的 getProName()
方法,但 Provider
对象本身是 null
。是因为在创建 Order
对象时,没有正确地初始化 provider
属性。
解决办法:确保SQL 查询能够正确地联接 smbms_bill
和 smbms_provider
表,并且在结果集中返回了 Provider
的所有必要字段。确保 providerId
在 smbms_bill
表中有相应的值,并且可以在 smbms_provider
表中找到对应的记录。
确保在查询结果映射时,正确地将 Provider
对象初始化到 Order
对象中。通过自定义 RowMapper
来处理这个映射(OrderRowMapper类),然后在 OrderDaoImpl
中使用这个 RowMapper
报错点:在进行数据查找时,根据OrderDao层中对返回的List数据来看,对于provider表查出来的数据只显示proName和proDesc,那么在设置需要什么则设置什么,如果全部打开则会报错
解决办法一:在OrderRowMapper类中只给Provider对象设置proName和proDesc
解决办法二:将OrderDao层的sql代码中对provider查找全部,并在OrderRowMapper类中给Provider对象设置全部字段信息或者设置需要的字段
OrderListController代码
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过getBean方法,获取到ioc容器中的对象
int pageNum = req.getParameter("pageNum") == null ? 1 : Integer.parseInt(req.getParameter("pageNum"));
int pageSize = req.getParameter("pageSize") == null ? 5 : Integer.parseInt(req.getParameter("pageSize"));
PageHelper.startPage(pageNum,pageSize) ;
OrderService orderService = (OrderService) getBean("orderService");
List<Order> orderList = orderService.list();
int totalCount = orderService.totalCount();
HttpSession session = req.getSession();
session.setAttribute("totalCount",totalCount);
session.setAttribute("pageSize",pageSize);
session.setAttribute("pageNum",pageNum);
session.setAttribute("orderList",orderList);
resp.sendRedirect(req.getContextPath() + "/admin_bill_list.jsp");
}
详情列表分页
问题:分页不能正确显示(该分页使用PageHelper)
OrderListController层获取页码数及每页显示条数传入PageHelper
OrderDaoImpl层中从数据库查找总记录数
@Override
public int totalCount() {
String sql = "select count(*) from smbms_bill";
int totalCount = jdbcTemplate.queryForObject(sql, Integer.class);
return totalCount;
}
在页面中渲染分页
报错点:在OrderDaoImpl层中的查询没有实现分页,需要在 SQL 查询中添加 LIMIT
和 OFFSET
子句,以便根据当前页码和每页大小限制结果集。
解决方案:
修改 OrderDaoImpl
的 list()
方法以支持分页: 更新 SQL 查询以使用分页。您可以在 SQL 查询中添加 LIMIT
和 OFFSET
@Override
public List<Order> list(int pageNum, int pageSize) {
String sql = "SELECT b.*, p.* FROM smbms_bill b JOIN smbms_provider p ON b.providerId = p.id LIMIT ? OFFSET ?";
int offset = (pageNum - 1) * pageSize;
return jdbcTemplate.query(sql, new Object[]{pageSize, offset}, new OrderRowMapper());
}
更新 OrderService
的 list()
方法: 确保在 OrderService
中的 list()
方法接受 pageNum
和 pageSize
参数,并将它们传递给 DAO 层
@Override
public List<Order> list(int pageNum, int pageSize) {
return orderDao.list(pageNum, pageSize);
}
分页效果显示
2.2 账单管理-账单信息增加
当用户点击添加数据,则跳转到新增页面,通过getParameter()方法拿回每个参数的值,并调用OrderServiceImpl的save()方法实现数据的增加,保存成功后重新跳转到账单列表页面,并刷新数据
OrderDaoImpl代码
jdbcTemplate.update(sql, ...)
用于执行更新操作(包括插入、更新和删除)。它会返回受影响的行数。
@Override
public int save(Order order) {
String sql = "INSERT INTO smbms_bill (billCode, productName, productDesc, productUnit, productCount, " +
"totalPrice, isPayment, createdBy, creationDate, providerId) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
// 使用 jdbcTemplate 执行插入操作
int save=jdbcTemplate.update(sql,
order.getBillCode(), // 账单编号
order.getProductName(), // 商品名称
order.getProductDesc(), // 商品描述
order.getProductUnit(), // 商品单位
order.getProductCount(), // 商品数量
order.getTotalPrice(), // 交易金额
order.getIsPayment(), // 是否付款
order.getCreatedBy(), // 创建者
order.getCreationDate(), // 创建时间
order.getProviderId() // 供应商ID
);
return save;
}
OrderServiceImpl代码
@Override
public int save(Order order) {
return orderDao.save(order);
}
AddOrderController代码
每个controller都需要继承BaseController
@WebServlet("/AddOrderController")
public class AddOrderController extends BaseController {
@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 {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
//获取数据
Order order = new Order();
order.setBillCode(req.getParameter("billCode"));
order.setProductName(req.getParameter("productName"));
order.setProductDesc(req.getParameter("productDesc"));
order.setProductCount(Double.parseDouble(req.getParameter("productCount")));
order.setProductUnit(req.getParameter("productUnit"));
order.setTotalPrice(Double.parseDouble(req.getParameter("totalPrice")));
order.setProviderId(Long.parseLong(req.getParameter("providerId")));
order.setIsPayment(Integer.parseInt(req.getParameter("isPay")));
order.setCreatedBy(Long.parseLong(req.getParameter("createdBy")));
order.setCreationDate(new Date());
//获取getBean
OrderService orderService = (OrderService) getBean("orderService");
int save = orderService.save(order);
//转发到账单列表页面
req.getRequestDispatcher("/OrderListController").forward(req, resp);
}
}
OrderAdd.jsp
隐藏域表单,用来获取创建者和创建时间,创建者身份通过request.getSession().getAttribute("userinfo")直接获取
<%@ page import="com.csi.domain.User" %>
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
<link type="text/css" rel="stylesheet" href="css/style.css"/>
<%
User userinfo = (User) request.getSession().getAttribute("userinfo");
%>
</head>
<body>
<div class="menu">
<form method="post" action="<%=request.getContextPath()%>/FindOrderByCondition" onsubmit="return checkit();">
商品名称:<input type="text" name="productName" class="input-text"/>
是否付款:<select name="payStatus">
<option value="">请选择</option>
<option value="1">已付款</option>
<option value="0">未付款</option>
</select>
<input type="submit" name="submit" value="组合查询" class="button"/>
</form>
</div>
<div class="main">
<div class="optitle clearfix">
<em><input type="button" name="button" value="添加数据" class="input-button"
onclick="location.href='modify.html'"/></em>
<div class="title">账单管理>></div>
</div>
<form method="post" action="<%=request.getContextPath()%>/AddOrderController">
<div class="content">
<table class="box">
<tr>
<td class="field">账单编号:</td>
<td><input type="text" name="billCode" class="text"/></td>
</tr>
<tr>
<td class="field">商品名称:</td>
<td><input type="text" name="productName" class="text"/></td>
</tr>
<tr>
<td class="field">商品描述:</td>
<td><textarea name="productDesc"></textarea></td>
</tr>
<tr>
<td class="field">交易数量:</td>
<td><input type="text" name="productCount" class="text"/></td>
</tr>
<tr>
<td class="field">商品单位:</td>
<td><input type="text" name="productUnit" class="text"/></td>
</tr>
<tr>
<td class="field">交易金额:</td>
<td><input type="text" name="totalPrice" class="text"/></td>
</tr>
<tr>
<td class="field">供应商ID:</td>
<td><input type="text" name="providerId" class="text"/></td>
</tr>
<tr>
<td class="field">是否付款:</td>
<td><select name="isPay">
<option value="-1">请选择</option>
<option value="1">已付款</option>
<option value="2">未付款</option>
</select></td>
</tr>
//隐藏域表单,用来获取创建者和创建时间,创建者身份通过request.getSession().getAttribute("userinfo")直接获取
<input name="createdBy" value="<%=userinfo.getUserRole()%>" class="text" type="hidden">
<input name="creationDate" value="<%=new Date()%>" class="text" type="hidden">
</table>
</div>
<div class="buttons">
<input type="submit" name="submit" value="确认" class="input-button" onclick="add()"/>
<input type="button" name="button" value="返回" class="input-button" onclick="history.back();"/>
</div>
</form>
</div>
<script>
function add(){
alert("添加成功!")
}
</script>
</body>
</html>
2.3 账单管理-账单信息删除
用户点击删除按钮,弹出提示“是否要删除”,如果是则将账单编号传入后台,调用delete()方法删除该数据,删除成功后刷新数据并跳转回账单列表页面
新增dao代码
@Override
public int delete(String billCode) {
String sql = "delete from smbms_bill where billCode=?";
//直接传入sql语句以及账单编号
int delete = jdbcTemplate.update(sql, billCode);
return delete;
}
新增service代码
@Override
public int delete(String billCode) {
return orderDao.delete(billCode);
}
DeleteController代码
@WebServlet("/DeleteController")
public class DeleteController extends BaseController {
@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 {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
OrderService orderService = (OrderService) getBean("orderService");
String billCode = req.getParameter("billCode");
orderService.delete(billCode);
req.getRequestDispatcher("/OrderListController").forward(req, resp);
}
}
在jsp页面中需要调用该DeleteController
为删除添加一个点击事件,为ture则删除,为false则返回。
2.4 账单管理-账单信息修改
当用户点击修改,则跳转到新增页面,通过getParameter()方法拿回每个参数的值,并调用OrderServiceImpl的update()方法实现数据的增加,保存成功后重新跳转到账单列表页面,并刷新数据
前端页面传值时,将商品名称及商品编号一并传回,修改表单显示时则会将商品名称及商品编号选软到页面中。
OrderModify.jsp
<%@ page import="com.csi.domain.User" %>
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
<link type="text/css" rel="stylesheet" href="css/style.css"/>
<%
User userinfo = (User) request.getSession().getAttribute("userinfo");
String billCode = request.getParameter("billCode");
String productName = request.getParameter("productName");
%>
</head>
<body>
<div class="menu">
<form method="post" action="<%=request.getContextPath()%>/FindOrderByCondition" onsubmit="return checkit();">
商品名称:<input type="text" name="productName" class="input-text"/>
是否付款:<select name="payStatus">
<option value="">请选择</option>
<option value="1">已付款</option>
<option value="0">未付款</option>
</select>
<input type="submit" name="submit" value="组合查询" class="button"/>
</form>
</div>
<div class="main">
<div class="optitle clearfix">
<em><input type="button" name="button" value="添加数据" class="input-button"
onclick="location.href='orderModify.jsp'"/></em>
<div class="title">账单管理>></div>
</div>
<form method="post" action="<%=request.getContextPath()%>/ModifyOrderController">
<div class="content">
<table class="box">
<tr>
<td class="field">账单编号:</td>
<td><input type="text" name="billCode" class="text" readonly value="<%=billCode%>"/></td>
</tr>
<tr>
<td class="field">商品名称:</td>
<td><input type="text" name="productName" class="text" value="<%=productName%>"/></td>
</tr>
<tr>
<td class="field">商品描述:</td>
<td><textarea name="productDesc"></textarea></td>
</tr>
<tr>
<td class="field">交易数量:</td>
<td><input type="text" name="productCount" class="text"/></td>
</tr>
<tr>
<td class="field">商品单位:</td>
<td><input type="text" name="productUnit" class="text"/></td>
</tr>
<tr>
<td class="field">交易金额:</td>
<td><input type="text" name="totalPrice" class="text"/></td>
</tr>
<tr>
<td class="field">是否付款:</td>
<td><select name="isPay">
<option value="-1">请选择</option>
<option value="1">已付款</option>
<option value="2">未付款</option>
</select></td>
</tr>
<input name="modifyBy" value="<%=userinfo.getUserRole()%>" class="text" type="hidden">
<input name="modifyDate" value="<%=new Date()%>" class="text" type="hidden">
</table>
</div>
<div class="buttons">
<input type="submit" name="submit" value="确认" class="input-button" onclick="modify()"/>
<input type="button" name="button" value="返回" class="input-button" onclick="history.back();"/>
</div>
</form>
</div>
<script>
function modify(){
alert("修改成功!")
}
</script>
</body>
</html>
修改成功后跳转到账单列表显示页面,修改结果同步刷新到页面
报错点
异常类型:
TransientDataAccessResourceException
表示在访问数据资源时发生了瞬时的异常,通常是由于参数传递不正确导致的。
SQL 语句:
SQL 更新语句中定义了 9 个参数(productName
, productDesc
, productUnit
, productCount
, totalPrice
, isPayment
, modifyBy
, modifyDate
, billCode
),但在执行时却提示参数索引超出范围,表明在传递参数时出现了问题。
参数索引超出范围:
异常信息中提到的“Parameter index out of range (10 > number of parameters, which is 9)”意味着在您的代码中,尝试访问第 10 个参数,但实际上只提供了 9 个参数