9、Spring基于JdbcTemplate的增删改查练习
Spring练习
1、环境的搭建
1.1、创建工程

1.2、静态页面的设计

1.3、导入需要的坐标
1、spring-context(spring的坐标肯定要嘛)
<!-- 导入Spring依赖环境 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.16</version>
</dependency>
2、spring-webmvc,SpringMVC
<!-- 导入springmvc依赖环境 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.16</version>
</dependency>
3、mysql,c3p0,JdbcTemplate工具
<!-- 导入mysql依赖环境 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- 引入c3p0数据源 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- jdbc工具JdbcTemplate -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.16</version>
</dependency>
4、JSTL,还有标签库的引用
<!-- jstl标签库和taglibs标签的依赖来使用jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
5、Servlet,配置DispatcherServlet的时候要用嘛(然后生效范围设置一下)
<!-- 导入Servlet的依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provide</scope>
</dependency>
6、JSON转换工具三个依赖包
<!-- JACSON转换JSON工具的包-->
<dependency>
<!-- 这个是最核心的 -->
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.4</version>
</dependency>
<!-- 注解相关的 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.4</version>
</dependency>
1.4、创建包结构
这个就不多bb了
- controller(web层)控制层
- service层(业务层)
- dao层(mapper层)持久层
- utils 工具类

1.5、导入数据库(实际上就是配置我们的数据源对象)
1、创建jdbc.properties配置文件,把我们数据库的连接信息先配置上
jdbc.driver="com.mysql.jdbc.driver"
jdbc.url="jdbc:mysql://localhost:3306/week9"
jdbc.user="root"
jdbc.password="010115"
2、创建applicationContext.xml配置文件
-
context命名空间,引入jdbc的配置文件
-

-
配置数据源对象dataSource
- 驱动传参
- url地址--需要操作哪个数据库
- 数据库用户名
- 数据库密码
-
<!-- 配置数据源对象 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 值注入-EL表达式 --> <property name="driverClass" value="${jdbc.driver}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean>
2、注入JdbcTemplate对象到IOC容器当中
<!-- JdbcTemplate注入 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 数据源对象注入 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
1.6、创建POJO类
-
User类,各属性分析一下
- id:序号
- name:用户的姓名
- username:用户名
- password:用户密码
- college:用户所在的学院
- dormitory:用户所在的宿舍
- sex:用户的性别
-
实体类的创建,截图
-

1.7、创建配置文件
配置文件总览
- spring的配置文件applicationContext.xml
- springMVC的配置文件spring-mvc.xml
- 数据库连接池的配置文件jdbc。properties
截图

1.8、配置web.xml
1、配置前端控制器-DispatcherServlet
-
<!-- 配置SpringMVC的前端控制器 --> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--当这个Servlet创建的时候,告诉他配置文件在哪里--> <!-- 初始化Servlet --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <!-- 当程序运行的时候就创建我们的前端控制器 --> <load-on-startup>1</load-on-startup> </servlet>
2、前端控制器的映射地址
-
<!-- 配置映射地址 --> <servlet-mapping> <!-- 缺省地址 --> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
3、配置监听器,spring会我们创建IOC容器和加载applicationContext的配置文件
<!-- 这里一定要配置监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
4、过滤器,识别post请求中携带的utf-8编码文件,生效区域全局
<!-- 配置全局过滤器识别UTF-8 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<!-- 设置生效的区域-全局生效 -->
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
1.9、配置spring-mvc的配置文件
1、引入mvc,context命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 引入context的命名空间进行我们的包扫描 -->
<context:component-scan base-package="com.waves.controller" />
2、引入springMVC的注解开发,controller请求转发,json自动转换等等
<mvc:annotation-driven/>
3、配置静态资源的扫描(全静态扫描)
<mvc:default-servlet-handler/>
4、视图解析器配置--String类型的返回值
<!-- 配置视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
1.10、applicationContext.xml配置文件配置
-
之前配置过了,就配置包扫描就行了,因为项目后面会使用注解开发
-
<!--配置组件扫描,告诉Spring我那些包下的组件需要被扫描和注入--> <context:component-scan base-package="com.waves"></context:component-scan>
1.11、遇到的报错原因
org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/jdbc.properties]


没写类路径下的jdbc.properties,他肯定找不到啊
2、业务分析
2.1、登录操作
-
index这个页面先重定向到登录页.html这个页面
-
<html> <body> <h2>Hello World!</h2> </body> <% response.sendRedirect(request.getContextPath() + "/login.html"); %> </html>
0、POJO类User遇见的报错
我在User类中设置了一个注解@Component,我想让他定义为一个组件,然后通过注入的方式将其注入到了UserDao这个对象中

但是,UserDao这个组件我已经在配置文件当中配置过了,并且,他的内部并没有属性User,和对应的set方法,因为我觉得不需要,但实际上就是吃了这个爆亏

1、dao层设计
-
对数据库进行查询,两个条件
-
判断用户名是否能查询到数据(单独设计查询)
-
/** * 查询用户名是否存在 * @param username 用户名 * @return 一条用户数据 */ User UserFound(String username); -
实现类实现该方法
-
// 判断用户是否存在 @Override public User UserFound(String username) { try{ // 使用JdbcTemplate访问数据库 user = template.queryForObject("select * from student where username=?", new BeanPropertyRowMapper<User>(User.class),username); }catch (DataAccessException e){ user = null; } return user; } -
二次判断,账号密码一起判断
-
/** * 用户登录操作 * @param username 用户名 * @param password 密码 * @return 一条用户的数据 */ User Login(String username,String password); -
实现类设计
-
// 用户登录 @Override public User Login(String username, String password) { try{ // 使用JdbcTemplate访问数据库 user = template.queryForObject("select * from student where username=? and password=?", new BeanPropertyRowMapper<User>(User.class), username,password); }catch (DataAccessException e){ user = null; } return user; }
2、业务层设计
-
一个Login方法
-
/** * 用户登录 * @param username 用户名 * @param password 用户密码 * @return 影响的行数 */ Integer Login(String username,String password); -
实现类实现过程
-
调用Dao层的判断方法和验证方法
-
@Override public Integer Login(String username,String password) { // 判断用户名是否存在 User user = userDao.UserFound(username); // System.out.println("查询到的用户数据为:"+user); if(user==null){ System.out.println("该数据不存在"); return 0; } // System.out.println("检测密码"); user = userDao.Login(username, password); // System.out.println("login的值为:"+login); if(user==null){ System.out.println("密码错误"); return 0; } return 1; }
3、控制层实现
html页面
- 获取表单的数据
- 发送ajax异步请求
- 接收后台返回的值
- 根据返回的状态码是否跳转页面
代码实现
-
<script type="text/javascript" src="./js/jQuery.js"></script> <script type="text/javascript"> $(function(){ $("#btn-login").click(function(){ //动态获取表单的数据 let account = $("#uname").val(); let password = $("#psw").val(); //发送ajax异步请求完成用户注册功能 $.ajax({ url:"http://localhost:8080/SpringProject_war_exploded/user/login", type: "POST", //可以,但不推荐 data:"username="+account+"&password="+password, dataType: "JSON", success: function(state){ console.log(state) if(state==200){ alert("登陆成功,spring") $(location).attr("href", "http://localhost:8080/SpringProject_war_exploded/jsp/DMT2.jsp") }else{ alert("登陆失败") } }, error: function(xhr){ alert("登录时产生未知异常"+xhr.status); } }); }) }) </script>
2.2、显示全部学生
1、dao层设计(规划SQL语句,查询数据库)
/**
* 查询全部的学生
* @return
*/
@Override
public List<User> AllStudent() {
List<User> users = template.query("select * from student",
new BeanPropertyRowMapper<User>(User.class));
return users;
}
2、service层设计(调用dao层,返回一个存储着全部用户信息的list集合)
/**
* 查询全部学生
*/
@Override
public List<User> AllStudent() {
List<User> users = userDao.AllStudent();
return users;
}
3、controller层设计(返回一个视图对象给前端)
关于ajax请求,已经做到了,但是,我不会jQuery
获取到后端给我的user对象
// 发送一个ajax请求
$.ajax({
type: "POST",
url: "http://localhost:8080/SpringProject_war_exploded/user/allStu1",
success: function(users) {
console.log(users)
for (var i = 0; i < users.length; i++) {
undefined
var username = users[i].username;
var pass = users[i].password;
var id = users[i].id;
}
},
error: function(xhr){
alert("登录时产生未知异常"+xhr.status);
}
});
-
返回一个视图对象,然后将集合存储到视图对象当中,视图对象返回的页面为DMT2页面
-
我设置了一个a标签,这个标签访问的地址就是我本地的那个DMT2的页面,通过视图对象返回,我在视图对象中存储了我这个users的集合,然后转发到DMT2页面,我感觉是可以重定向的,但是太麻烦了我就不写了
-

-
controller代码
-
// 查看全部学生 @RequestMapping("/allStu") public ModelAndView AllStudent(){ List<User> users = userService.AllStudent(); ModelAndView model = new ModelAndView(); model.addObject("users",users); model.setViewName("DMT2"); return model; } -
最终效果
-

-
点击查看所有,访问我们的那个请求地址selectStu
-

2.3、添加学生
1、页面设计
-
点击添加学生按钮,输入所有信息,发送表单数据
-
<input type="button" id = "btn" name="add" value="添加学生" /> <a href="http://localhost:8080/SpringProject_war_exploded/user/allStu">查看所有</a> <form action="${pageContext.request.contextPath}/user/addStu" method="post" class="addStu"> 学号: <input type="text" name="uname"/><br> 密码: <input type="password" name="password"/><br> 姓名: <input type="text" name="name"/><br> 学院: <input type="text" name="school"/><br> 宿舍: <input type="text" name="domitory"/><br> 班级: <input type="text" name="cls"/><br> 性别: <input type="radio" name="sex" value="man"/>男 <input type="radio" name="sex" value="woman"/>女 <br> <input type="submit" value="提交" style="margin-left:50px;"/> </form>
2、dao层设计
-
规划SQL语句,添加学生(主键自增)
-
// 添加学生 int row1 = template.update("insert into student values(null,?,?,?,?,?,?)", user.getName(), user.getUsername(), user.getPassword(), user.getSex(), user.getCollege(), user.getDormitory() );
3、service层设计(判断用户名是否重复)
- 接收前端传递过来的user对象,先判断用户名是否重复
- 不重复就开始添加新用户
代码设计--返回给控制层一个状态码
// 添加学生
@Override
public String AddStu(User user) {
// 判断这个用户是否注册过
User user1 = userDao.UserFound(user.getUsername());
if(user1!=null){
// 用户已注册
System.out.println("Username已存在");
return "402";
}
int row = userDao.addStu(user);
if(row!=1){
System.out.println("注册的时候发生未知异常!");
return "403";
}
return "200";
}
4、控制层设计
-
获取前端发送过来的表单数据
-
调用业务层
-
转发到原始页面
-
// 添加学生 @RequestMapping("/addStu") // 用户登录 public String addStu(User user){ System.out.println(user); userService.AddStu(user); return "DMT2"; }
2.4、删除学生
1、jsp页面设计,在通过查询后台数据显示出全部数据到前台的时候,为删除和修改的a标签设置一个点击事件
-
<a href="javascript:void(0);">修改</a> <a href="javascript:void(0);" onclick="deleteUser('${user.getId()}')">删除</a></td> -
点击事件直接跳转到需要访问的请求资源路径下
-
function deleteUser(userid){ if(confirm("您确定要删除嘛?")){ location.href ="${pageContext.request.contextPath}/user/deleteStu/"+userid; } }
2、dao层设计
-
获取到业务层传递的uid,在数据库中进行删除的操作
-
/** * 删除学生实现 * @param uid 要删除的用户id * @return 影响的行数 */ @Override public int deleteStu(String uid) { int row1 = 0; try { int row = template.update("delete from student where id = ?", uid); row1 = row; }catch (InvalidResultSetAccessException e){ row1 = 0; throw new RuntimeException(e); }catch (DataAccessException e){ row1 = 0; throw new RuntimeException(e); } return row1; }
3、service层设计
-
调用dao层的删除学生的方法
-
@Override public String deleteStu(String uid) { // 调用dao层 int row = userDao.deleteStu(uid); if(row!=1){ return "402"; } return "200"; }
4、controller设计
-
获取前端传递过来的请求参数,前端传递过来的参数是Restful类型的,所以我们之前的需要修改一下
-
// 删除学生,Restful风格,先整个参数占位符 @RequestMapping("/deleteStu/{id}") // @PathVariable占位符进行解析 public String deleteStu(@PathVariable("id") String id){ System.out.println("获取到的uid为:"+id); // 调用业务层 userService.deleteStu(id); // 重定向到这个视图 return "DMT2"; }
3、SpringMVC拦截器
3.1、图解

3.2、SpringMVC拦截器interceptor和Filter的关系
1、Filter
- SpringMVC的拦截器就相当于之前web阶段的Filter,对访问的目标资源进行一个干预,地址写对了多半可以访问到,但是,在Filter这个阶段里面,即便是你的资源路径,请求路径写对了,也不一定能访问到,因为中间夹杂着许多许多的拦截器;总的来说就是这个大哥,放行,你就可以访问到目标资源,不放行,那就不放呗
2、interceptor
- 二者的用法基本一样,在之前我们设置的Controller中,只要我那个地址没写错基本百分百能访问到,但是配置了这个interceptor之后,就不一定了,因为,从业务角度设计来讲,举个简单的例子,你没登录能直接修改你的账号密码吗,这显然是不符合逻辑的,而interceptor,就是为了解决这个而存在的
3、链的概念
- interceptor就是一条拦截器链条,当你访问一个资源的时候,需要按照规定好的拦截器链条,一步一步的访问,直到所有的拦截器链条被完整的运行完毕之后,依然可以继续通行的时候,才可以访问到最终的目标地址
3.3、Filter和interceptor的区别(还是有区别的)

3.4、快速入门
1、图解

2、创建拦截器类并实现HandlerInterceptor接口
// 创建我的拦截器类并实现HandlerInterceptor接口
public class MyIntercepter implements HandlerInterceptor {
}
- 实现这个接口的三个主要方法
- preHandle: 在请求执行之前,执行这个方法
- postHandle: 在请求执行后,视图返回之前执行此方法
- afterCompletion: 在整个请求结束之后执行这个方法--做收尾工作
// 创建我的拦截器类并实现HandlerInterceptor接口
public class MyIntercepter implements HandlerInterceptor {
// 在请求执行之前,执行这个方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle......");
return false;
}
// 在请求执行后,视图返回之前执行此方法
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle.....");
}
// 在整个请求结束之后执行这个方法--做收尾工作
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion.....");
}
}
3、spring-mvc配置我们的拦截器
使用的标签mvc:interceptors,说明肾么?这个东西可以配很多拦截器,我们现在只配置我们的
<!-- 配置自定义拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 对那些资源需要进行一个拦截 -->
<mvc:mapping path="/**"/>
<!-- 这里使用我们配置的拦截器的全限定名 -->
<bean class="com.waves.com.waves.intercepter.MyIntercepter"></bean>
</mvc:interceptor>
</mvc:interceptors>
4、测试--我们以首页的登录方法作为测试
- 这里有句话,看我们控制台的打印

4.1关于preHandle中的返回值的解析
默认返回为false,如果返回的是false,那么后面的步骤都不需要执行了,直接全部被拦截,我们将其改为true
- 开始测试首页的登录

5、执行顺序
- proHandler=>请求的资源路径=>postHandler=>afterCompletion
6、三个方法的下详细解析
6.1、preHandler
- 这个方法体内部有三个参数(HttpServletRequest request, HttpServletResponse response, Object handler),如果返回值是false,是不是可以通过request,或者说response进行转发或者重定向,跳转到其他页面,而不是你要访问的那个页面
- 他的返回类型是布尔类型的,当返回值为false的时候,表示请求结束,后续的拦截器和控制层的方法都不会在执行,当返回值为true的时候,就会调用下一个拦截器的preHandle方法

6.2、postHandler
- 这儿方法内部的四个参数,前两个不必多说,Hnadler是处理器,而第四个参数是我们的模型对象,该方法在请求资源路径执行之后开始执行该方法,他拿到你这个模型对象之后是不是也可以进行视图跳转的修改,从而让你不能够访问到想访问的资源?
- 该方法是在当前请求被进行处理之后才调用的,前提的前提就是preHandle的返回值必须是true,而且他会在DispatcherServlet进行视图返回渲染之前被调用,也就是说,Controller层被调用了,进行了视图渲染,但是我可以在他渲染之后,再一次对这个视图进行二次渲染,也就是可以对ModelAndView进行操作
HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView
6.3、afterCompletion(最后执行,规划异常等等,这个用的很少)
3.5、测试原理阶段
1、controller层设计
- 设计一个资源路径,请求路径为user/intercast
- 他这里需要获得一个请求参数,名字为username

2、拦截器层设计(preHandle)
-
先通过request获取到我们的那个请求参数,然后开始对参数进行一个判定,满足条件的话,返回true,否则就返回false
-
preHandler代码设计
-
// 在请求执行之前,执行这个方法 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle....."); // 先通过request获得一个请求参数,如果没有携带这个请求参数 String username = request.getParameter("username"); // 或者请求参数不是我想要的 if(!username.equals("zhangsan")){ // 重定向到一个页面 response.sendRedirect("http://localhost:8080/SpringProject_war_exploded/error.jsp"); // 直接返回false return false; } return true; } -
返回true的话我就可以正常访问到我刚刚设计的那个interceptorTest的页面,如果返回false就重定向到我设计error页面!
3、测试阶段
-
发送一个请求,这个参数满足我们的条件
-
-

-
不满足我们条件的参数
-

-
查看结果
-

-

3.6、拦截器链条
1、可以设置多个拦截器,并且,拦截器遵循一个规则,先进去,后出来
-
配置第二个拦截器,拦截器输出语句为xxxxx222222.....
-

-

2、测试


浙公网安备 33010602011771号