提示:自我练习之后参考进行自主整合
代码要多练,自己练,独立练,才能有效,不能做脱离视频课件的三不知
这篇文章能够帮助你了解基础框架SSM的整合及进行相应的增删改查操作
本质:Spring接管一切,代码更加简洁。
所需工具:
工具 | 版本 |
---|---|
IDEA | 2021.3 |
mysql | 5.7 |
maven | 3.8.4 |
物理建模顾名思义就是在数据库中创建一个具体的table表,在数据库中实实在在存在的。本文以t-emp表为例。
下图给出数据库名称和表中字段名。
在idea中创建对应工程或者模块
包名 | 作用 |
---|---|
entity | 存放实体类 |
handler | 存放控制器类 |
service | 存放service类和其实现类 |
mapper | 存放mapper接口 |
test | 存放测试类 |
在entity
包中创建和数据库对应的实体类Employee
代码示例如下:
package com.atguigu.ssm.entity;
public class Emp {
private Integer empId;
private String empName;
private Double empSalary;
public Emp() {
}
public Emp(Integer empId, String empName, Double empSalary) {
this.empId = empId;
this.empName = empName;
this.empSalary = empSalary;
}
public Integer getEmpId() {
return empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Double getEmpSalary() {
return empSalary;
}
public void setEmpSalary(Double empSalary) {
this.empSalary = empSalary;
}
@Override
public String toString() {
return "Emp{" +
"empId=" + empId +
", empName='" + empName + '\'' +
", empSalary=" + empSalary +
'}';
}
}
将整个SSM框架整合所需依赖添加,这里属于基础依赖,后续其他依赖需要按相应功能使用添加。
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.1version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>5.3.1version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleaf-spring5artifactId>
<version>3.0.12.RELEASEversion>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.3version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.0.31version>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiter-apiartifactId>
<version>5.7.0version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.3.1version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.6version>
dependency>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.2.0version>
dependency>
dependencies>
加入配置文件主要是为了能够调试代码,输出日志,进而了解异常等问题所在根源。
注意:配置文件全部放在resources目录下。
loback.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>
encoder>
appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
root>
<logger name="org.springframework.web.servlet.DispatcherServlet" level="DEBUG"/>
configuration>
全局日志级别我们一般都设置为debug。其余根据需求进行设置。比如说test测试类。
代码如下(示例):
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:13306/mybatis_example
jdbc.username=root
jdbc.password=abc123
spring-presist.xml
创建spring-persist.xml配置文件引入jdbc.properties要注意引入context空间,如果不引入,接下来的操作会报各种错误。比如说:
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
bean>
注意:这里使用的是druid 数据库连接池 DruidDataSource
并且 必须写成 driverClassName
<logger name="com.atguigu.ssm.test.SSMTest" level="DEBUG"/>
@Autowired
private DataSource dataSource;
注意:引入的是import javax.sql.DataSource;
@SpringJUnitConfig(locations = {"classpath:spring-persist.xml"})
public class SSMTest {
@SpringJUnitConfig -----spring整合junit5单元测试注解,指定spring配置文件位置,自动加载上下文。
完整代码如下:
@SpringJUnitConfig(locations = {"classpath:spring-persist.xml"})
public class SSMTest {
@Autowired
private DataSource dataSource;
//使用日志进行输出,需要在logback.xml中设置类的日志级别为DEBUG
Logger logger = LoggerFactory.getLogger(getClass());
@Test
public void testConn() throws SQLException {
Connection connection = dataSource.getConnection();
//使用日志级别输出
logger.debug(connection.toString());
}
}
注意:
我们使用的日志文件导入的包是:org.slf4j.Logge
mybatis-config.xml
主要作用:
代码示例如下:
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
<typeAliases>
<package name="com.atguigu.ssm.entity"/>
typeAliases>
configuration>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<property name="mapUnderscoreToCamelCase" value="true"/>
bean>
property>
<property name="typeAliasesPackage" value="com/atguigu/ssm/entity"/>
<property name="mapperLocations" value="classpath:mappers/*Mapper.xml"/>
/*Mapper.xml代表对所有的mapper配置文件有效
<property name="dataSource" ref="druidDataSource"/>
<mybatis-spring:scan base-package="com.atguigu.ssm.mapper"/>
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.atguigu.ssm.mapper"/>
bean>
完整配置如下:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<property name="mapUnderscoreToCamelCase" value="true"/>
bean>
property>
<property name="typeAliasesPackage" value="com/atguigu/ssm/entity"/>
<property name="mapperLocations" value="classpath:mappers/*Mapper.xml"/>
<property name="dataSource" ref="druidDataSource"/>
<mybatis-spring:scan base-package="com.atguigu.ssm.mapper"/>
public interface EmpMapper {
List<Emp> selectAll();
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssm.mapper.EmpMapper">
<select id="selectAll" resultType="Emp">
select emp_id,emp_name,emp_salary from t_emp
select>
@SpringJUnitConfig(locations = {"classpath:spring-persist.xml"})
public class SSMTest {
@Autowired
private DataSource dataSource;
@Autowired
private EmpMapper empMapper;
//使用日志进行输出,需要在logback.xml中设置类的日志级别为DEBUG
Logger logger = LoggerFactory.getLogger(getClass());
@Test
public void testTx(){
List<Emp> empList = empService.getAll();
for (Emp emp : empList) {
System.out.println("emp = " + emp);//打 soutv 字母即为快捷键,可以快速生成foreach循环
}
}
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"/>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<context:component-scan base-package="com.atguigu.ssm.service"/>
public interface EmpService {
List<Emp> getAll();
@Service
public class EmpServiceImpl implements EmpService {
@Autowired
private EmpMapper empMapper;
@Override
public List<Emp> getAll() {
return empMapper.selectAll();
}
@Transactional(readOnly = true) //为当前业务层添加事务 “只读”
public List<Emp> getAll() {
@SpringJUnitConfig(locations = {"classpath:spring-persist.xml"})
public class SSMTest {
@Autowired
private DataSource dataSource;
@Autowired
private EmpMapper empMapper;
@Autowired
private EmpService empService;
//使用日志进行输出,需要在logback.xml中设置类的日志级别为DEBUG
Logger logger = LoggerFactory.getLogger(getClass());
@Test
public void testTx(){
List<Emp> empList = empService.getAll();
for (Emp emp : empList) {
System.out.println("emp = " + emp);//打 soutv 字母即为快捷键,可以快速生成foreach循环
}
}
注意
需要使用 @Autowired注解装配service接口
在pom.xml中将当前module的打包方式修改为war。
<packaging>warpackaging>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-persist.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<servlet>
<servlet-name>dispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>characterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
<init-param>
<param-name>forceRequestEncodingparam-name>
<param-value>trueparam-value>
init-param>
<init-param>
<param-name>forceResponseEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>characterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<filter>
<filter-name>hiddenHttpMethodFilterfilter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class>
filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
<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:mvc="http://www.springframework.org/schema/mvc"
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 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.atguigu.ssm.handler"/>
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".html"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateMode" value="HTML5"/>
bean>
property>
bean>
property>
bean>
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
beans>
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页title>
head>
<body style="text-align: center">
<h1>首页h1>
body>
html>
<mvc:view-controller path="/" view-name="portal"/>
小结:
经过以上SSM整合的准备工作后,主要是对框架环境的搭建,下面开始进行分页显示数据以及基本的增删改查操作。
下面的操作使用 restful 风格,具体功能清单
功能 | URL地址 | 请求方式 |
---|---|---|
访问首页 | / | view-controller |
查询全部数据 | /get/all | GET |
查询分页数据 | /get/page/{pageNo} | GET |
删除 | /emp/{empId}/{pageNo} | DELETE |
跳转到添加页面 | /emp/add | view-controller |
提交表单(执行保存) | /emp | POST |
跳转到更新页面 | /emp/{empId}/{pageNo} | GET |
执行更新 | /emp | PUT |
@Controller
public class EmpHandler {
@Autowired
private EmpService empService;
为了让springMVC找到控制器,当前的控制器就必须是IOC容器中的组件,因此需要加上注解 @Controller
前面我们在进行测试的时候就已经将mapper接口和mapper.xml以及empService接口及实现类中查询全部数据的代码已经完成了,现在我们只需要编写EmpHandler。
@Controller
public class EmpHandler {
@Autowired
private EmpService empService;
@RequestMapping("/get/all")
public String getAll(Model model) {
//1.查询数据
List<Emp> empList = empService.getAll();
//2.存入模型
model.addAttribute("empList", empList);
return "emp-list";
}
portal.xml
<a th:href="@{/get/all}">显示全部数据a><br>
从请求域中获取请求参数:
${ }
内置对象:
#lists
th:each
emp-list.html完整代码如下:
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<style type="text/css">
table {
border-collapse: collapse;
margin: 0px auto 0px auto;
}
table th, td {
border: 1px solid black;
text-align: center;
}
style>
head>
<body>
<table>
<tr>
<th>IDth>
<th>NAMEth>
<th>SALARYth>
tr>
<tbody th:if="${#lists.isEmpty(empList)}">
<tr>
<td colspan="3">抱歉,没有查询到任何数据!td>
tr>
tbody>
<tbody th:if="${not #lists.isEmpty(empList)}">
<tr th:each="emp : ${empList}">
<td th:text="${emp.empId}">这里显示员工IDtd>
<td th:text="${emp.empName}">这里显示员工Nametd>
<td th:text="${emp.empSalary}">这里显示员工SALARYtd>
tr>
tbody>
table>
<a th:href="@{/}">回首页a>
body>
html>
添加了一个回到首页的超链接
提出想法:
当数据很多的时候,我们适当分页并设置简单导航来使得查看数据更加便捷。
注意:
这里不详细介绍,可以去相关链接查看,此处只做相关功能测试。
代码重工-分页相关知识
在pom.xml配置文件中
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.2.0version>
dependency>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<props>
<prop key="reasonable">trueprop>
<prop key="helperDialect">mysqlprop>
props>
property>
bean>
array>
property>
<a th:href="@{/get/page/1}">显示分页数据a><br/>
rest风格: @{/get/page/{pageNo}}
这里我们默认设置点击超链接,跳转页面显示从第一页开始。
//显示分页数据
@RequestMapping("/get/page/{pageNo}")
public String getPage(
@PathVariable("pageNo") Integer pageNo,
Model model) {
//1. PageInfo 对象封装了和分页相关的所有信息
PageInfo<Emp> pageInfo = empService.getPageInfo(pageNo);
//2.将 PageInfo 对象存入模型
model.addAttribute("pageInfo", pageInfo);
//3.返回页面
return "emp-page";
}
PageInfo<Emp> getPageInfo(Integer pageNo);
@Override
public PageInfo<Emp> getPageInfo(Integer pageNo) {
//1、确定每页显示数据的条数
int pageSize = 5;
//2、设定分页数据:开启分页功能。开启后,后面执行的 SELECT 语句会自动被附加 LIMIT 子句,
//而且会自动查询总记录数
PageHelper.startPage(pageNo, pageSize);
//3、正常执行查询
List<Emp> empList = empMapper.selectAll();
//4、封装为 PageInfo 对象返回
return new PageInfo<>(empList);
}
mapper接口和mapper.xml配置文件不用配置,利用查询数据的方法,只是做了分页显示。
创建 emp-page.html
pageInfo.list
<tbody th:if="${#lists.isEmpty(pageInfo.list)}">
<tr>
<td colspan="4">抱歉,没有查询到任何数据!td>
tr>
tbody>
<tbody th:if="${not #lists.isEmpty(pageInfo.list)}">
<tr th:each="emp : ${pageInfo.list}">
<td th:text="${emp.empId}">这里显示员工IDtd>
<td th:text="${emp.empName}">这里显示员工Nametd>
<td th:text="${emp.empSalary}">这里显示员工SALARYtd>
tr>
tbody>
pageInfo.hasPreviousPage
& pageInfo.hasNextPage
<span th:if="${pageInfo.hasPreviousPage}">
<a th:href="@{/get/page/1}">首页a>
<a th:href="@{/get/page/}+${pageInfo.prePage}">上一页a>
span>
<span th:if="${pageInfo.hasNextPage}">
<a th:href="@{/get/page/}+${pageInfo.nextPage}">下一页a>
<a th:href="@{/get/page/}+${pageInfo.pages}">最后一页a>
span>
<span th:text="${pageInfo.pageNum}+'/'+${pageInfo.pages}">span>
navigator : ${pageInfo.navigatepageNums}
<span th:each="navigator : ${pageInfo.navigatepageNums}">
<a th:if="${navigator != pageInfo.pageNum}"
th:href="@{/get/page/}+${navigator}"
th:text="'['+${navigator}+']'">a>
span>
<span th:if="${navigator == pageInfo.pageNum}" th:text="'['+${navigator}+']'">span>
输入文本框
<tr>
<td colspan="4">
<input id="jumpToPageNumInput" @click="jumpToPageNumInput" type="text" name="jumpToPageNum" placeholder="请输入你想直接跳转的页码"/>
td>
tr>
配套js(使用了vue)
引入vue.js
<script type="text/javascript" th:src="@{/static/js/vue.js}">script>
编辑点击响应函数
<script type="text/javascript">
var vue = new Vue({
el: "#pageTable", // el 指定Vue对象关联的HTML元素的id
methods: { //配合 v-on 声明事件响应函数
jumpToPageNumInput:function (event) {//表示当前事件
//获取页码文本框的元素对象
var jumpToPageNum = document.getElementById("jumpToPageNumInput");
//给页码文本框绑定值改变响应函数
jumpToPageNum.onchange = function (){
//触发值改变响应函数后,获取当前文本框中用户输入的值
var targetNum = this.value;
//检测用户输入的数据是否是数字
if(isNaN(targetNum)){
//如果不是数字则恢复文本框
this.value = "";
//当前函数结束
return ;
}
//如果用户输入合法,则跳转页面
//[[@{/get/page/}]] 表示解析 thymeleaf 表达式
window.location.href = "[[@{/get/page/}]]" + targetNum;
}
}
}
});
</script>
<a class="deleteA" @click="deleteEmployee"
th:href="@{/emp/}+${emp.empId}+'/'+${pageInfo.pageNum}">deletea>
<form id="delete_form" method="post">
<input type="hidden" name="_method" value="delete"/>
form>
<script type="text/javascript" th:src="@{/static/js/vue.js}">script>
<script type="text/javascript">
var vue = new Vue({
el: "#pageTable", // el 指定Vue对象关联的HTML元素的id
methods: { //配合 v-on 声明事件响应函数
deleteEmp: function (event) { //event 表示当前事件
//通过document对象在整个文档范围内查找对象
//使用var 关键字接收document.getElementById("delete_form")方法的返回值
var delete_form = document.getElementById("delete_form");
//将触发事件的超链接的href属性为表单的action属性赋值
delete_form.action = event.target.href;
//提交表单
delete_form.submit();
//阻止超链接的默认跳转行为(点击超链接,会跳转页面,这是超链接的默认行为,需取消。)
event.preventDefault();
}
}
});
</script>
超链接
<a onclick="convertMethod(this, event)" th:href="@{/emp/}+${emp.empId}+'/'+${pageInfo.pageNum}">删除a>
配套js
<script type="text/javascript">
function convertMethod(anchorElement, event) {
// 获取超链接原本要访问的目标地址
var targetURL = anchorElement.href;
// 获取表单对象
var delete_form = document.getElementById("delete_form");
// 把超链接原本要访问的地址设置给表单的 action 属性
delete_form.action = targetURL;
// 提交表单
delete_form.submit();
// 取消控件的默认行为:让超链接不会跳转
event.preventDefault();
}
</script>
delete删除操作中可能出现的错误
@RequestMapping(value = "/emp/{empId}/{pageNo}", method = RequestMethod.DELETE)
// @DeleteMapping("/emp/{empId}/{pageNo}")
public String deleteEmployee(
@PathVariable("empId") Integer empId,
@PathVariable("pageNo") Integer pageNo) {
// 调用 Service 方法执行删除
empService.deleteEmployee(empId);
// 以重定向方式回到分页页面,避免刷新浏览器重新执行删除操作
return "redirect:/get/page/" + pageNo;
}
void deleteEmployee(@Param("empId") Integer empId);
@Transactional(
propagation = Propagation.REQUIRES_NEW, //设置事务传播行为 :当前方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起
rollbackFor = Exception.class //出现哪些异常可以回滚
)
@Override
public void deleteEmployee(Integer empId) {
empMapper.deleteEmployee(empId);
}
void deleteEmployee(Integer empId);
<delete id="deleteEmployee">
delete from t_emp where emp_id = #{empId}
delete>
在表头处添加一个添加按钮
<tr>
<th>OPTIONS(<a th:href="@{/emp/add}">adda>)th>
tr>
<mvc:view-controller path="/emp/add" view-name="emp-add"/>
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form th:action="@{/emp}" method="post">
姓名:<input type="text" name="empName"><br/>
工资:<input type="text" name="empSalary"><br/>
<button type="submit">保存button>
form>
body>
html>
//提交添加的表单(保存数据),默认前往最后一页显示
@RequestMapping(value = "/emp",method = RequestMethod.POST)
public String addEmp(){
empService.addEmployee();
return "redirect:/get/page/"+ Integer.MAX_VALUE;
}
service接口
void addEmp(Emp emp);
service实现类
@Transactional(
propagation = Propagation.REQUIRES_NEW, //设置事务传播行为 :当前方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起
rollbackFor = Exception.class //出现哪些异常可以回滚
)
@Override
public void addEmp(Emp emp) {
empMapper.addEmp(emp);
}
EmpMapper
void addEmp(Emp emp);
EmpMapper.xml
<insert id="addEmp">
insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})
insert>
<a th:href="@{/emp/}+${emp.empId}+'/'+${pageInfo.pageNum}">updatea>
//更新操作一:回显数据(在表单中显示数据,相当于查询,所以是GET)
@RequestMapping(value = "/emp/{empId}/{pageNo}",method = RequestMethod.GET)
public String updateEmp(
@PathVariable("empId") Integer empId,
@PathVariable("pageNo") Integer pageNo,
Model model){
//1.根据 empId 查询得到用来回显表单数据的实体类对象
Emp emp = empService.selectEmpById(empId);
//2.存入模型中,即向域对象中共享数据,方便获取
model.addAttribute("emp",emp);
//3.返回表单页面修改 emp-edit ,进行修改操作
return "emp-edit";
}
Emp getEmpById(Integer empId);
@Transactional(readOnly = true)
@Override
public Emp getEmpById(Integer empId) {
return empMapper.getEmpById(empId);
}
Emp getEmpById(Integer empId);
<select id="getEmpById" resultType="Emp">
select emp_id,emp_name,emp_salary from t_emp where emp_id = #{empId}
select>
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form th:action="@{/emp}" method="post">
<input type="hidden" name="_method" value="put">
<input type="hidden" name="pageNo" th:value="${pageNo}">
<input type="hidden" name="empId" th:value="${emp.empId}">
姓名:<input type="text" name="empName" th:value="${emp.empName}"/><br/>
工资:<input type="text" name="empSalary" th:value="${emp.empSalary}"/><br/>
<button type="submit">更新button>
form>
body>
html>
//更新操作二:提交表单
@RequestMapping(value = "/emp",method = RequestMethod.PUT)
public String update(Emp emp,
/*
因为请求路径中没有携带pageNo(占位符中没有)
所以不能通过@PathVariable来给控制器方法的形参赋值,所以只能通过@RequestParam将请求参数和控制器的
形参创建映射关系,因此需要我们在提交表单的时候,将pageNo随表单一起提交给服务器
因此需要有一个隐藏域来传输pageNo
*/
@RequestParam("pageNo") Integer pageNo){
empService.update(emp);
return "redirect:/get/page/"+pageNo;
}
void update(Emp emp);
@Transactional(
propagation = Propagation.REQUIRES_NEW, //设置事务传播行为 :当前方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起
rollbackFor = Exception.class //出现哪些异常可以回滚
)
@Override
public void update(Emp emp) {
empMapper.updateByEmpId(emp);
}
void updateByEmpId(Emp emp);
<update id="updateByEmpId">
update t_emp set emp_name = #{empName},emp_Salary=#{empSalary} where emp_id = #{empId}
update>
在完成此案例中可能用到的相关知识合集:
thymeleaf之获取请求参数
thymeleaf之内置对象
thymeleaf-表达式语法
代码重工-thymeleaf详解
w3school-HTML知识详解
thymeleaf的简单用法-常用标签
restful风格
restful案例
springmvc-路径中的占位符
js中的window.onload
在dom元素加载前Cannot find element
springmvc获取请求参数
代码重工-分页相关知识
model.addattribute & request.setattribute区别
thymeleaf从入门到吃灰
delete删除操作中可能出现的错误
思维导图最终版
例如:以上就是今天要讲的内容,本文仅仅简单介绍了SSM整合及其ACRUD操作的使用。