ORM
Object Relational Mapping 对象关系映射
创建Java类的对象,将其属性和数据库表中的字段名之间构建映射关系。
这样通过操作该类创建的一个对象,就能操作数据库中对应的数据表。
ORM框架就是对象关系映射框架,用于简化JDBC。
主流的ORM框架有Hibernate、JPA、MyBatis、MyBatisPlus等
Mybatis
Mybatis是一个半自动化的ORM框架。原本叫做ibatis,后来升级改名为MyBatis。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
使用XML文件或注解的形式,将SQL语句映射为持久层(dao层)中的方法。
官网:https://mybatis.org/mybatis-3/zh/index.html
特点
方便
简化JDBC
解除SQL语句与业务代码的耦合
sql和java代码分离,sql写在xml文件中,方便扩展维护。
支持动态SQL
不同的情况下构造不同的SQL
主流
SSM中的M
SSM项目的搭建
整体流程
1.创建基于Maven的webapp项目。
2.修改web.xml版本为4.0 ,创建java、resoureces目录、项目包结构、web-inf下的页面目录。
3.导入依赖:
spring-webmvc
mybatis
mybatis-spring
mysqldruid
spring-jdbc
jstl
<!--SpringWebMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.22</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<!--Spring集成MyBatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<!--数据库连接池Druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!--SpringJDBC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
<!--MySQL-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!--jstl-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
4.配置Spring:
在resoureces下创建application.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">
<!--扫描使用了Spring注解的根包-->
<context:component-scan base-package="com.hqyj.ssm01"></context:component-scan>
</beans>
配置web.xml
设置全局参数: ,读取application.xml文件
设置全局监听器:ContextLoaderListener,初始化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">
<!--通过全局监听器初始化Spring容器-->
<listener>
<listenerclass>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--定义全局参数读取Spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
</web-app>
5.配置SpringMVC:
创建springmvc.xml
扫描控制层所在包
设置内部资源视图解析器:InternalResourceViewResolver,设置前后缀
<?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">
<!--扫描使用了SpringMVC注解的根包-->
<context:component-scan base-package="com.hqyj.ssm01.controller">
</context:component-scan>
<!--设置内部资源视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--设置页面前缀和后缀-->
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
配置web.xml
设置请求分发器:DispatcherServlet,在其中读取springmvc.xml配置文件
过滤器解决请求中文乱码:CharacterEncodingFilter
<?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">
<!--通过全局监听器初始化Spring容器-->
<listener>
<listenerclass>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--定义全局参数读取Spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
<!--配置DispatcherServlet-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--初始化参数读取SpringMVC配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--过滤器解决请求中文乱码-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
6.配置MyBatis
在resoureces中创建mybatis-config.xml(官网模板)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--设置-->
<settings>
<!--开启驼峰命名映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启SQL日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
在application.xml中注入
数据库连接池Druid:DruidDataSource
SQL会话工厂:SqlSessionFactoryBean
映射扫描配置器:MapperScannerConfigurer
<?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">
<!--扫描使用了Spring注解的根包-->
<context:component-scan base-package="com.hqyj.ssm01">
</context:component-scan>
<!--注入Druid数据源DruidDataSource-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/herodb?serverTimezone=Asia/Shanghai"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!--注入MyBatis核心对象SqlSessionFactoryBean-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<!--设置数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--设置MyBatis配置文件路径-->
<property name="configLocation" value="classpath:mybatisconfig.xml"></property>
<!--设置MyBatis的sql映射文件路径-->
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean>
<!--扫描项目中的dao层目录-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--设置MyBatis核心对象SqlSessionFactoryBean的名字-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<!--dao层根包-->
<property name="basePackage" value="com.hqyj.ssm01.dao"></property>
</bean>
</beans>
7.执行sql语句
创建dao层接口,定义方法
//实体类
package com.hqyj.ssm01.entity;
public class Hero {
private int id;
private String name;
private String sex;
private String position;
private int jiage;
private String shelfTime;
//省略get/set
}
//dao层接口
package com.hqyj.ssm01.dao;
import com.hqyj.ssm01.entity.Hero;
import org.springframework.stereotype.Repository;
import java.util.List;
/*
* 使用MyBatis时,数据访问层/持久层/dao层,只需定义为接口
* */
@Repository
public interface HeroDao {
/*查询所有*/
List<Hero> queryAll();
}
//service层
package com.hqyj.ssm01.service;
import com.hqyj.ssm01.dao.HeroDao;
import com.hqyj.ssm01.entity.Hero;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class HeroService {
@Autowired
private HeroDao heroDao;
public List<Hero> queryAll(){
return heroDao.queryAll();
}
}
//controller层
package com.hqyj.ssm01.controller;
import com.hqyj.ssm01.entity.Hero;
import com.hqyj.ssm01.service.HeroService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
@RequestMapping("/hero")
public class HeroController {
@Autowired
private HeroService heroService;
@RequestMapping("/queryAll")
public String queryAll(Model model){
List<Hero> list = heroService.queryAll();
model.addAttribute("list",list);
return "heroList";
}
}
在resoureces下创建mapper目录创建sql映射文件xx.xml(官网模板),在其中定义sql语句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace是该映射文件对应的dao层接口全限定名-->
<mapper namespace="com.hqyj.ssm01.dao.HeroDao">
<!--查询select标签-->
<!--id对应dao层中的某个方法名-->
<!--如果实体类中的属性名和表中的字段名一致,加入resultType属性,设置实体的全限定名-->
<!--
<select id="queryAll" resultType="com.hqyj.ssm01.entity.Hero">
select * from hero
</select>
-->
<!--resultMap标签用于给指定的实体类设置返回结果集映射-->
<!--id属性自定义该映射名称-->
<!--type属性指定实体类-->
<resultMap id="heroMap" type="com.hqyj.ssm01.entity.Hero">
<!--主键字段用id标签,其他字段用result标签-->
<!--id和result标签都有两个属性 property表示实体类中的属性名 column表示表中的字段名-->
<id property="id" column="hid"></id>
<result property="jiage" column="price"></result>
<!--如果遇到下划线转驼峰命名,无需在这里自定义映射,可以在mybatis的配置文件中设置开启驼峰命名映射-->
<!--<result property="shelfTime" column="shelf_time"></result>-->
</resultMap>
<!--如果实体中的属性和表中的字段名不一致,使用resultMap属性设置自定义的结果集映射-->
<select id="queryAll" resultMap="heroMap">
select * from hero
</select>
</mapper>
heroList.jsp页面
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<style>
table{
border-collapse: collapse;
}
td{
width: 100px;
height: 20px;
border: 1px solid skyblue;
}
</style>
</head>
<body>
<table>
<c:forEach var="hero" items="${list}">
<tr>
<td>${hero.id}</td>
<td>${hero.name}</td>
<td>${hero.sex}</td>
<td>${hero.position}</td>
<td>${hero.jiage}</td>
<td>${hero.shelfTime}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
SSM项目搭建补充
通过db.properties文件保存连接数据库的信息
.properties文件称为属性文件,其中的数据以键值对(key=value)的形式保存
在resources目录下新建db.properties文件
DB_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver
DB_URL=jdbc:mysql://localhost:3306/bookdb?serverTimezone=Asia/Shanghai
DB_USERNAME=root
DB_PASSWORD=root
在application.xml中读取properties文件
<!--读取properties文件-->
<context:property-placeholder location="classpath:db.properties">
</context:property-placeholder>
读取时使用EL表达式访问其中的键
<!--Druid数据源DruidDataSource-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="druidDataSource">
<property name="driverClassName" value="${DB_DRIVER_CLASS_NAME}"></property>
<property name="url" value="${DB_URL}"></property>
<property name="username" value="${DB_USERNAME}"></property>
<property name="password" value="${DB_PASSWORD}"></property>
</bean>
MyBatis配置文件常用设置
<?xml version="1.0" encoding="UTF-8" ?>
<!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"/>
<!--开启SQL日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/></settings>
</configuration>
MyBatis基本增删改查
dao层
package com.hqyj.ssm02.dao;
import com.hqyj.ssm02.entity.BookInfo;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface BookInfoDao {
//查询所有
List<BookInfo> queryAll();
//根据id删除
int delete(int id);
//添加
int insert(BookInfo bookInfo);
//根据id查询
BookInfo findById(int no);
//修改时,参数通常为一个完整的修改对象
int update(BookInfo bookInfo);
//批量删除
//分页查询
//关键字分页
//如果dao层中某个方法不止1个参数,需要给每个参数添加@Param("参数名")注解,给该参数命名
//命名后,才能在mybatis的sql映射文件中使用该参数,即#{参数名}
//如这里的newPrice,在sql中用#{newPrice}获取
int update(@Param("newPrice") int bookPrice,@Param("newNum") intbookNum,@Param("updateId") int bookId);
}
sql映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hqyj.ssm02.dao.BookInfoDao">
<!--查询所有-->
<select id="queryAll" resultType="com.hqyj.ssm02.entity.BookInfo">
select *
from book_info
</select>
<!--根据id查询-->
<select id="findById" resultType="com.hqyj.ssm02.entity.BookInfo">
select *
from book_info
where book_id = #{no}
</select>
<!--根据id删除-->
<delete id="delete">
delete
from book_info
where book_id = #{id}
</delete>
<!--添加-->
<insert id="insert">
insert into book_info
values (null, #{typeId}, #{bookName}, #{bookAuthor}, #{bookPrice}, #{bookNum}, #{publisherDate}, #{bookImg})
</insert>
<!--修改-->
<update id="update">
update book_info
set book_price=#{newPrice},
book_num=#{newNum},
type_id=#{typeId}
where book_id = #{updateId}
</update>
</mapper>
解决无法引入静态资源的问题
在webapp目录下,新建一个目录,通常命名为static。将项目中的静态资源文件都保存于此。
在springmvc.xml中
<!--映射静态资源目录-->
<!--location表示要映射的静态资源目录-->
<!--mapping表示最终通过哪种方式进行访问。这里表示只要以/static开头的请求,都可以访问静态
资源目录-->
<mvc:resources mapping="/static/**" location="/static/"></mvc:resources>
<!--开启注解驱动,有这个标签,SpringMVC就能区分哪个是资源文件,哪个是Controller-->
<mvc:annotation-driven></mvc:annotation-driven>
导入BootStrap的样式和JS文件
<%--导入bootstrap的css文件--%>
<link href="${pageContext.request.contextPath}/static/bootstrap-3.4.1-dist/css/bootstrap.min.css" rel="stylesheet" type="text/css">
<%--导入jquery--%>
<script src="${pageContext.request.contextPath}/static/bootstrap-3.4.1-dist/js/jquery-3.6.2.min.js"></script>
<%--导入boostrap的js文件--%>
<script src="${pageContext.request.contextPath}/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
在SpringMVC中,让某个控制层中的方法返回JSON格式的数据
添加依赖
<!--jackson:将数据转换为JSON格式-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
@ResponseBody注解
该注解可以加在类或方法上
如果加在方法上,表示该方法的返回值类型为JSON格式
如果加在类上,表示该类中的所有方法的返回值类型为JSON格式
package com.hqyj.ssm02.controller;
import com.hqyj.ssm02.entity.BookType;
import com.hqyj.ssm02.service.BookTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
@RequestMapping("/bookType")
public class BookTypeController {
@Autowired
private BookTypeService bookTypeService;
//如果controller的某个方法返回一个JSON字符串,需要使用@ResponseBody
@RequestMapping("/queryAllToJson")
@ResponseBody//表示该方法无论返回值是什么,都返回JSON格式字符串
public List<BookType> queryAllToJson(){
List<BookType> list = bookTypeService.queryAll();
return list;
}
}
SSM项目中使用Ajax
ajax依赖于jquery,所以先保证页面中存在jquery.js。
$.ajax({
url:"访问地址",
data:{
"提交的参数名":"实际值",
"提交的参数名":"实际值"
},
type:"get/post",
success:function(res){
//成功后的回调函数,res为访问后的结果,必须是json格式
}
});
在前端页面中使用ajax访问controller时,controller的返回值必须是一个JSON格式的字符串。
所以controller中的方法上要加入@ResponseBody注解
在SpringMVC中使用Session
方式一:@SessionAttributes注解
由于SSM项目中,没有使用servlet,所以不能通request.getSession()方法来获取session对象。
在控制器Controller中,在类上加入@SessionAttributes注解@SessionAttributes({"参数1","参数2"})表示在session中保存两个参数
再在某个方法中,通过Model对象调用addAttribute("参数1",对象)方法将指定对象保存到session中
使用和销毁
@Controller
@RequestMapping("/sysAdmin")
//如果要将数据保存到session中,先使用该注解定义session中的参数名
@SessionAttributes({"sysAdmin"})
public class SysAdminController {
@Autowired
SysAdminService sysAdminService;
@RequestMapping("/login")
@ResponseBody
public SysAdmin login(SysAdmin sysAdmin,Model model) {
SysAdmin login = sysAdminService.login(sysAdmin);
//将登录成功的对象保存到session中
model.addAttribute("sysAdmin",login);
return login;
}
/*登出时销毁session*/
@RequestMapping("/logout")
public String logout(SessionStatus sessionStatus) {
//在方法中使用SessionStatus参数表示session状态对象
//调用setComplete()方法,将session设置为完成状态
sessionStatus.setComplete();
return "redirect:/login";
}
}
方式二:HttpServletRequest参数
给项目添加javax.servlet.api依赖,给controller中某个方法添加 HttpServletRequest参数后,获取session使用
<!--如果要使用servlet相关内容-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
使用和销毁
@Controller
@RequestMapping("/sysAdmin")
//如果要将数据保存到session中,先使用该注解定义session中的参数名
@SessionAttributes({"sysAdmin"})
public class SysAdminController {
@Autowired
SysAdminService sysAdminService;
@RequestMapping("/login")
@ResponseBody
public SysAdmin login(SysAdmin sysAdmin,HttpServletRequest req) {
SysAdmin login = sysAdminService.login(sysAdmin);
//将登录成功的对象保存到session中
req.getSession().setAttribute("sysAdmin",login);
return login;
}
/*登出时销毁session*/
@RequestMapping("/logout")
public String logout(HttpServletRequest req) {
req.getSession().invalidate();
return "redirect:/login";
}
}
拦截器
每次请求controller时,都要经过的一个类。
当一个项目中有过滤器、拦截器和controller时的执行流程
实现过程
1.导入servlet依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
2.自定义一个类,实现拦截器HandlerInterceptor接口
其中有三个default方法可以重写
preHandle
在发送请求后,DispatcherServlet解析控制器中某个RequestMapping之前执行的方法
该方法返回true时,请求才能继续
postHandle
在preHandle方法返回值为true后执行
在DispatcherServlet解析控制器中某个RequestMapping之后执行
afterCompletion
在preHandle方法返回true后执行
在解析视图后执行的方法
这里只需重写preHandle方法即可
package com.hqyj.ssm02.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
* 自定义拦截器,用户拦截未登录时的请求
* */
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
//登录成功后,会在session中保存一个名为sysAdmin的对象
Object sysAdmin = request.getSession().getAttribute("sysAdmin");
//如果有对象,说明登录成功,可以放行return true
if(sysAdmin!=null){
return true;
}else{
response.sendRedirect(request.getContextPath()+"/login");
}
System.out.println(requestURI+"试图访问,拦截成功");
return false;
}
}
3.在springmvc.xml中配置拦截器
<!--配置拦截器们-->
<mvc:interceptors>
<!--配置某个拦截器-->
<mvc:interceptor>
<!--设置要拦截的请求,这里的/**表示拦截一切请求-->
<mvc:mapping path="/**"/>
<!--设置不拦截的请求-->
<!--放行登录和注册页-->
<mvc:exclude-mapping path="/login"/>
<mvc:exclude-mapping path="/register"/>
<!--放行静态资源-->
<mvc:exclude-mapping path="/static/**"/>
<!--放行用户模块-->
<mvc:exclude-mapping path="/sysAdmin/**"/>
<!--注入指定的拦截器-->
<bean class="com.hqyj.ssm02.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
单元测试JUnit
1.导入依赖
如果只在普通的Maven项目中使用,只需导入该依赖
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
如果要在Spring环境下使用,还需
<!--spring集成JUnit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.23</version>
<scope>test</scope>
</dependency>
2.创建单元测试类所在目录
在项目的src目录下新建test目录,会自动在其中创建java目录
3.使用
在test/java目录下,新建一个类
在类上加入
@ContextConfiguration("classpath:application.xml")
@RunWith(SpringJUnit4ClassRunner.class)
在类中创建公开的无返回值的无参数方法,加入@Test注解
在该方法上右键运行,运行的是当前方法,在类中空白处右键运行,运行的是当前类中的所有方法。
import com.hqyj.ssm02.dao.BookInfoDao;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
//解析Spring配置文件
@ContextConfiguration("classpath:application.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class Test {
/*
* 单元测试的方法必须是无参数无返回值的公共方法
* */
@org.junit.Test
public void fun(){
System.out.println("单元测试");
}
@Autowired
private BookInfoDao bookInfoDao;
@org.junit.Test
public void test1(){
System.out.println(bookInfoDao.queryAll());
}
}
查询
单表查询
dao中的方法
List<BookType> queryAll();
sql映射文件
当数据库表中的字段名和实体属性一致或开启了驼峰命名映射,使用resultType
<select id="queryAll" resultType="com.xxx.entity.BookType">
select * from book_type
</select
当数据库表中的字段名和实体属性不一致,使用resultMap
<select id="queryAll" resultMap="map">
select * from book_type
</select>
<!--自定义结果集映射-->
<resultMap id="map" type="com.xxx.entity.BookType">
<!--主键使用id标签,其他字段使用result标签-->
<!--column对应字段名 property对应属性名-->
<id column="type_id" property="typeId"></id>
<result column="type_name" property="typeName"></result>
</resultMap>
多表查询
多对一查询
多对一表示多个从表实体对应一个主表实体。如多本图书对应一个图书类型。
在从表实体中,额外添加一个属性:对应的主表实体对象
public class BookInfo {
private int bookId;
private int typeId;
private String bookName;
private String bookAuthor;
private int bookPrice;
private int bookNum;
private String publisherDate;
private String bookImg;
//多对一查询,额外添加外键对应的实体对象
private BookType bt;
}
dao中的方法
List<BookInfo> queryAll();
sql映射文件
关联查询:构建特殊的sql语句
适合外键对应的表中字段比较少的情况下使用
<!--sql语句除了查询自身表之外,还要查询关联的表中的字段,将其命名为"实体对象.属性"-->
<select id="queryAll" resultType="com.xxx.entity.BookInfo">
select bi.*,type_name as 'bt.typeName' from book_info bi,book_type bt
where bi.type_id=bt.type_id
</select
连接查询(不建议)
<select id="queryAll" resultMap="booksMap">
select *
from book_info bi,
book_type bt
where bi.type_id = bt.type_id
</select>
<resultMap id="booksMap" type="com.hqyj.ssm02.entity.BookInfo">
<!--主键用id标签,其他字段用result标签赋值-->
<id property="bookId" column="book_id"></id>
<result property="bookName" column="book_name"></result>
<result property="typeId" column="type_id"></result>
<result property="bookAuthor" column="book_author"></result>
<result property="bookPrice" column="book_price"></result>
<result property="bookNum" column="book_num"></result>
<result property="bookImg" column="book_img"></result>
<result property="publisherDate" column="publisher_date"></result>
<!--外键对应的实体对象使用association标签,通过property属性对应实体对象名-->
<association property="bt" >
<select id="queryAll" resultMap="booksMap">
select *
from book_info bi,
book_type bt
where bi.type_id = bt.type_id
</select>
<resultMap id="booksMap" type="com.hqyj.ssm02.entity.BookInfo">
<!--主键用id标签,其他字段用result标签赋值-->
<id property="bookId" column="book_id"></id>
<result property="bookName" column="book_name"></result>
<result property="typeId" column="type_id"></result>
<result property="bookAuthor" column="book_author"></result>
<result property="bookPrice" column="book_price"></result>
<result property="bookNum" column="book_num"></result>
<result property="bookImg" column="book_img"></result>
<result property="publisherDate" column="publisher_date"></result>
<!--外键对应的实体对象使用association标签,通过property属性对应实体对象名-->
<association property="bt" >
</resultMap>
子查询
适合外键对应的表中字段比较多的情况
<!--1.查询从表-->
<select id="queryAll" resultMap="booksMap">
select * from book_info
</select>
<!--2.自定义结果集映射,使用type_id进行子查询,下方的sql-->
<resultMap id="booksMap" type="com.hqyj.ssm02.entity.BookInfo">
<!--由于使用type_id进行了子查询,所以如果要给type_id赋值,需要再次进行映射-->
<result column="type_id" property="typeId"></result>
<!--外键对应的实体对象,使用association赋值-->
<!--如果这里的子查询来自当前映射文件-->
<association property="bt" column="type_id" select="findTypeByTypeId">
</association>
<!--如果这里的子查询来自于其他dao中的方法-->
<!--<association property="bookType" column="type_id"
select="com.hqyj.ssm02.dao.BookTypeDao.findById"></association>-->
</resultMap>
<!--3.根据type_id查询完整对象-->
<select id="findTypeByTypeId" resultType="com.hqyj.ssm02.entity.BookType">
select * from book_type where type_id=#{typeId}
</select>
一对多查询
一对多表示一个主表实体对应多个从表实体。如一个图书类型对应多本图书。
在主表对应的实体类中,额外添加一个属性:多个从表对象的集合
public class BookType {
private int typeId;
private String typeName;
private List<BookInfo> books;
}
dao中的方法
BookType queryBooksByType(int typeId);
sql映射文件
连接查询
<!--一对多查询,方式一:连接查询-->
<select id="queryBooksByType" resultMap="testMap">
select *
from book_info bi
inner join book_type bt on bi.type_id = bt.type_id
where bi.type_id = #{typeId}
</select>
<!--自定义结果集映射-->
<resultMap id="testMap" type="com.hqyj.ssm02.entity.BookType">
<id property="typeId" column="type_id"></id>
<result property="typeName" column="type_name"></result>
<!--集合类型的属性,使用collection标签,使用ofType设置集合中的对象类型-->
<collection property="books" ofType="com.hqyj.ssm02.entity.BookInfo">
<id column="book_id" property="bookId"></id>
<result column="book_name" property="bookName"></result>
<result column="type_id" property="typeId"></result>
<result column="book_author" property="bookAuthor"></result>
<result column="book_num" property="bookNum"></result>
<result column="book_price" property="bookPrice"></result>
<result column="book_img" property="bookImg"></result>
<result column="publisher_date" property="publisherDate"></result>
</collection>
</resultMap>
子查询
<!--一对多查询,方式二:子查询-->
<!--1.根据类型编号查询自身表-->
<select id="queryBooksByType" resultMap="testMap">
select *
from book_type
where type_id = #{typeId}
</select>
<!--2.设置结果集映射-->
<resultMap id="testMap" type="com.hqyj.ssm02.entity.BookType">
<id column="type_id" property="typeId"></id>
<result column="type_name" property="typeName"></result>
<!--集合对象,使用collection标签。使用type_id字段执行子查询getBooksByTypeId,将
结果映射到books属性-->
<collection property="books" column="type_id" select="getBooksByTypeId">
</collection>
</resultMap>
<!--3.子查询,根据类型编号查询所有对应的图书集合-->
<select id="getBooksByTypeId" resultType="com.hqyj.ssm02.entity.BookInfo">
select *
from book_info
where type_id = #{typeId}
</select>
总结
多对一
如果查询要以从表数据为主,关联主表相应的数据时,属于多对一查询。如查询所有图书的同时,
显示其类型。
建议使用子查询或自定义特殊的sql语句。都需要在从表实体中添加一个主表对象属性。
主表字段比较少,建议使用自定义特殊的sql语句,保证字段重命名为"主表对象.属性"
主表字段比较多,建议使用子查询,使用 标签映射主表对象属性
一对多
如果查询要以主表数据为主,关联该主键对应的从表实体对象的集合时,属于一对多查询。如查询
某个类型,同时显示该类型下的所有图书。
建议使用子查询。在主表实体中添加一个从表集合属性。
使用 标签映射从表集合属性
多条件查询
参考#{}和${}的用法
#{}和${}
在mybatis的sql映射文件中,可以使用#{}和${}表示sql语句中的参数。
#{}相当于预处理,会将参数用?占位后传值
{}相当于拼接,会将参数原样拼接
通常使用#{},防止sql注入
-- #{}会自动用''将参数引起来,如参数为admin
select * from user where username=#{username}
select * from user where username='admin'
-- ${}会将参数原样拼接,如参数为admin
select * from user where username=${username}
select * from user where username=admin
${}使用情况
如有批量删除的sql,需要传递" 1,2,3,4,5"参数
delete from user where id in (1,2,3,4,5)
int delelteByIds(String ids);
//如实际传递的是"1,2,3,4,
5"
<delete id="deleteByIds">
delete from user where id in (${ids})
</delete>
#{}的使用
如果dao层接口中的方法参数为一个原始类型或字符串时
sql语句#{}中的参数是接口中方法的形参名
public interface UserDao{
User findById(int userId);
}
<select id="findById" resultType="com.xxx.entity.User">
select * from user where id=#{userId}
</select>
如果dao层接口中的方法参数为一个实体对象时
sql语句#{}中的参数必须是接口中方法参数的某个属性
public interface UserDao{
User login(User user);
}
<select id="login" resultType="com.xxx.entity.User">
select * from user where username=#{username} and password=#{password}
</select>
如果dao层接口中的方法参数不止一个时
给每个参数添加@Param("自定义参数名")
sql语句#{}中的参数必须是@Param注解中自定义的名称
public interface UserDao{
User update(@Param("bianhao")int id,@Param("mima")String password);
}
<update id="update">
update user set password=#{mima} where id=#{bianhao}
</update>
动态SQL
搭配 用于修改
<!--动态SQL:set-if用于修改-->
<update id="testUpdate">
update book_info
<set>
book_id=#{bookId},
<if test="bookName!=null">
book_name = #{bookName},
</if>
<if test="bookPrice!=null">
book_price = #{bookPrice}
</if>
</set>
where book_id=#{bookId}
</update>
搭配 用于查询、修改、删除时的条件
<select id="queryByCondition" resultType="com.xxx.entity.BookInfo">
select * from book_info
<where>
<if test="bookName!=null">
book_name = #{bookName}
</if>
<if test="bookAuthor!=null">
and book_author = #{bookAuthor}
</if>
<if test="typeId!=null">
and type_id = #{typeId}
</if>
</where>
</select>
搭配 可以替换set-if和where-if
该标签有四个属性
prefix 表示如果trim标签中有if条件满足,就在整个trim部分添加指定前缀
suffix 表示如果trim标签中有if条件满足,就在整个trim部分添加指定后缀
prefixOverrides 表示去除整个trim部分多余的前缀
suffixOverrides 表示去除整个trim部分多余的后缀
使用trim实现修改
可以修改所有字段,也可以修改部分字段
<update id="testUpdate">
update book_info
<!--prefix="set"表示在所有内容前加入set关键字-->
<!--suffixOverrides=","表示所有内容之后如果有多余的逗号,去掉逗号-->
<trim prefix="set" suffixOverrides=",">
book_id=#{bookId},
<if test="bookName!=null">
book_name = #{bookName},
</if>
<if test="bookAuthor!=null">
book_author = #{bookAuthor},
</if>
<if test="bookNum!=null">
book_num = #{bookNum},
</if>
<if test="bookPrice!=null">
book_price = #{bookPrice},
</if>
<if test="publisherDate!=null">
publisher_date = #{publisherDate}
</if>
</trim>
where book_id=#{bookId}
</update>
使用trim标签实现多条件查询
<select id="queryByCondition" resultType="com.hqyj.ssm02.entity.BookInfo">
SELECT bi.*,type_name as 'bookType.typeName' FROM book_info bi,book_type bt
<!--prefix="where"表示在所有内容前加入where关键字-->
<!--prefixOverrides="and"表示所有内容之前如果有多余的and,去掉and-->
<!--suffix="order by book_id desc"表示在所有内容之后加入order by book_id desc-->
<trim prefix="where" prefixOverrides="and" suffix="order by book_id desc">
bi.type_id=bt.type_id
<if test="bookName!=null">
and book_name like concat ('%',#{bookName},'%')
</if>
<if test="bookAuthor!=null">
and book_author like concat ('%',#{bookAuthor},'%')
</if>
<if test="typeId!=0">
and bt.type_id =#{typeId}
</if>
</trim>
</select>
多选删除具体实现
页面核心js
$(function () {
//一键全选按钮
$("#checkAll").click(function () {
var state = this.checked;
$(".checkDel").each(function () {
this.checked = state;
});
});
//删除所选按钮
$("#deleteAll").click(function () {
//定义保存id的数组
var ids = [];
//获取当前被选中的复选框所在的tr
let $tr = $(".checkDel:checked").parent().parent();
//遍历所选的tr
$tr.each(function () {
//获取id对应的td的值
var id = $(this).children("td:eq(1)").text();
//保存到数组中
ids.push(id);
});
//至少选中一项
if (ids.length == 0) {
alert("请至少选中一项");
return;
}
if (!confirm("确认要删除这" + ids.length + "条数据吗")) {
return;
}
//使用ajax访问controller删除所选
$.ajax({
url: "${pageContext.request.contextPath}/bookInfo/deleteByChecked",
data: {
"ids": ids
},
type: "post",
//ajax提交数组,需要添加一个参数
traditional: true,
success: function () {
location.reload();
}
});
});
});
dao
//批量删除
int deleteByIds(@Param("idList") List<Integer> idList);
mapper.xml
<!--批量删除-->
<delete id="deleteByIds">
delete from book_info where book_id in
<foreach collection="idList" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
service
public void deleteByChecked(Integer[] ids) {
//将数组转换为集合
List<Integer> list = Arrays.asList(ids);
bookInfoDao.deleteByIds(list);
}
controller
@RequestMapping("/deleteByChecked")
public String deleteByChecked(Integer[] ids){
bookInfoService.deleteByChecked(ids);
return "redirect:queryAll";
}
分页
使用分页组件PageHelper
1.导入依赖
<!--分页组件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.2</version>
</dependency>
2.在mybatis的配置文件中
<!--设置分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--保证翻页不会超出范围-->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
3.使用
通过PageHelper类调用静态方法startPage(当前页数,每页显示的记录数)开启分页
查询所有,返回集合
创建PageInfo分页模型对象,构造方法的参数为查询出的集合,设置泛型,
//定义当前页和每页显示的数量
int pageNum=1;
int size=10;
//开启分页
PageHelper.startPage(pageNum,size);
//正常调用查询,返回查询到的数据集合
BookInfo bookInfo = new BookInfo();
bookInfo.setTypeId(1);
List<BookInfo> list = bookInfoDao.queryByCondition(bookInfo);
//创建分页模型对象,构造方法的参数为查询出的结果,设置泛型,
PageInfo<BookInfo> pageInfo = new PageInfo<>(list);
//此时分页相关数据都保存在pageInfo对象中
System.out.println("总记录数"+pageInfo.getTotal());
System.out.println("最大页数"+pageInfo.getPages());
System.out.println("当前页"+pageInfo.getPageNum());
System.out.println("当前容量"+pageInfo.getSize());
System.out.println("当前分页数据"+pageInfo.getList());
System.out.println("是否有上一页"+pageInfo.isHasPreviousPage());
System.out.println("是否有下一页"+pageInfo.isHasNextPagexml
System.out.println("是否是首页"+pageInfo.isIsFirstPage());
System.out.println("是否是尾页"+pageInfo.isIsLastPage());