框架是一套规范。
实际是他人实现的一系列接口和类的集合。通过导入对应框架的jar文件(Maven项目导入对应的依赖),进行适当的配置,就能使用其中的所有内容。开发者可以省去很多模板代码
Spring
是一个轻量级开源的Java框架,是一个管理项目中对象的容器,也是其他框架的粘合器,目的就是对项目进行解耦
核心:IOC控制反转和AOP面向切面编程
控制反转是一种思想,就是让创建对象的控制权由自身交给第三方,控制翻转这种思想,通过依赖注入的方式实现
bean标签常用属性
属性 | 作用 |
class | 定义类的全限定名 |
id | 定义对象的名称 |
lazy-init | 设置是否为懒加载,默认值为false,在解析配置文件时就会创建对象。设置为true表示懒加载,只有在getBean()时才会创建对象。 |
scope | 单例/原型模式。默认值为singleton,表示单例模式,只会创建一个对象。设置为prototype,表示原型模式,每次调用getBean()就会创建一个对象 |
init-method | 初始化方法,在创建完该对象时自动调用的方法,该方法只能是无参方法,该属性的值只需要写方法名即可 |
destory-method | 销毁时触发的方法。Spring容器关闭时自动调用的方法,该方法只能是无参方法,只有在单例模式下有效 |
属性注入
给某个bean标签添加属性的两种方式:setter注入和构造器注入
setter注入
该方法注入属性时,类中必须要有set方法
该标签的name属性通常表示该对象的某个属性名,但实际是setXXX()方法中的XXX单词。
该标签的value属性表示给该类中的某个属性赋值,该属性的类型为原始类型或String。
<!--注入Car类对象,并用set方式注入其属性-->
<bean class="com.hqyj.SetterInject.Car" id="c">
<!--该属性是字符串类型或原始类型,使用value-->
<property name="brand" value="宝马"></property>
<property name="color" value="白色"></property>
</bean>
构造器注入
这种方式注入属性时,类中必须有相应的构造方法(有参)
该标签的name属性表示构造方法的参数名,index属性表示构造方法的参数索引。
赋值时,原始类型和字符串用value,引用类型用ref。
<!--注入Person类对象 并用构造方法注入其属性-->
<bean class="com.hqyj.SetterInject.Person" id="p2">
<!--constructor-arg表示构造方法参数name是参数名 index是参数索引-->
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg index="1" value="23"></constructor-arg>
<!--c是Car类的对象-->
<constructor-arg name="car" ref="c" ></constructor-arg>
</bean>
复杂属性注入
public class Movie {
private String movieName;//电影名
private String director;//导演
private int duration;//时长
private List<String> playerList;//主演
private String movieType;//电影类型
private String showTime;//放映时间:最终格式为yyyy/MM/dd HH:mm:ss
//忽略了setter/getter/gouzao方法等
}
List类型属性注入
<!--注入movie对象-->
<bean class="com.hqyj.SetterInject.Movie" id="movie1">
<property name="movieName" value="夏洛特烦恼"></property>
<property name="director" value="闫飞彭大魔"></property>
<property name="duration" value="87"></property>
<!--List类型属性赋值-->
<property name="playerList">
<!--使用list标签-->
<list>
<!-- 如果集合中保存的是引用类型,使用ref标签-->
<!--如果集合中保存的是原始类型或字符申,使用value标签-->
<value>沈腾</value>
<value>马丽</value>
<value>艾伦</value>
</list>
</property>
<property name="movieType" value="戏剧"></property>
<property name="showTime" value="2020/06/10 0:0:0"></property>
</bean>
Set类型属性注入
//宠物类
public class Pet {
private String petType;
private String petNickName;
private int petAge;
public Pet(String petType, String petNickName, int petAge) {
this.petType = petType;
this.petNickName = petNickName;
this.petAge = petAge;
}
//省略了setter/getter/toString方法
public Pet() {
}
}
//宠物店类
public class PetShop {
private String shopName;
private Set<Pet> petList;
public PetShop(String shopName, Set<Pet> petList) {
this.shopName = shopName;
this.petList = petList;
}
//省略了setter/getter/toString方法
public PetShop() {
}
}
注入:
<bean class="com.hqyj.SetInject.Pet" id="pet1">
<property name="petType" value="二哈"></property>
<property name="petNickName" value="乐乐"></property>
<property name="petAge" value="3"></property>
</bean>
<bean class="com.hqyj.SetInject.Pet" id="pet2">
<property name="petType" value="金毛"></property>
<property name="petNickName" value="linck"></property>
<property name="petAge" value="2"></property>
</bean>
<bean class="com.hqyj.SetInject.PetShop" id="petShop">
<property name="shopName" value="狗狗乐园"></property>
<property name="petList">
<set>
<ref bean="pet1"></ref>
<ref bean="pet2"></ref>
</set>
</property>
</bean>
Map属性注入
<!--实体类-->
public class Cinema {
private String name;
<!--定义Map保存上映时间表,键是影厅号,值是电影对象-->
private Map<Integer,Movie> timeTable;
public Cinema(String name, Map<Integer, Movie> timeTable) {
this.name = name;
this.timeTable = timeTable;
}
public Cinema() {
}
<!--省略了toString()、getter()、getter()方法-->
}
<!--注入Cinema对象-->
<bean class="com.hqyj.SetterInject.Cinema" id="cinema">
<property name="name" value="万达影城"></property>
<property name="timeTable">
<!--Map类型属性赋值,标明键和值的类型-->
<map key-type="java.lang.Integer" value-type="com.hqyj.SetterInject.Movie">
<!--entry标签表示键值对如果键值对都是原始类型或字符串,使用key和value-->
<!--如果键值对中有引用类型,使用key-ref或value-ref-->
<entry key="1" value-ref="movie1"></entry>
<entry key="2" value-ref="movie2"></entry>
</map>
</property>
</bean>
现在时间注入
<!--注入SimpleDateFormat对象,设置日期格式-->
<bean class="java.text.SimpleDateFormat" id="sdf">
<!--如果构造方法注入时,该构造方法只有一个参数,可以不用name或index-->
<constructor-arg index="0" value="yyyy/MM/dd"></constructor-arg>
</bean>
<!--注入Date对象--> .
<bean class="java.util.Date" id="now"></bean>
<bean class="com.hqyj.SetterInject.Movie" id="movie1">
<property name="类中的时间属性名">
<bean factory-bean="sdf" factory-method="format">
<constructor-arg ref="now"></constructor-arg>
</bean>
</property>
</bean>
属性自动注入autowire
实现自动注入
在某个bean标签中,加入autowire属性,设置值为"byName"或"byType",通常设置为"byType"
public class Car {
private String brand;
private String color;
public Car(String brand, String color) {
this.brand = brand;
this.color = color;
}
public Car() {
}
//省略了setter/getter/toString方法
}
//Person类
public class Person {
private String name;
private int age;
private Car car;
public Person(String name, int age,Car car) {
this.name = name;
this.age = age;
this.car = car;
}
public Person() {
}
//省略了setter/getter/toString方法
}
配置文件:
<!--在容器中注入Car类型的bean-->
<bean class="com.hqyj.SetterInject.Car" id="car">
<property name="brand" value="宝马"></property>
<property name="color" value="白色"></property>
</bean>
<!--注入Person类的bean-->
<!--autowire="byType "表示自动检测该类中是否需要使用引用类型参数,如果容器中正好有唯一的一个对应类型的bean, 就会自动赋值给对应的属性-->
<bean class="com.hqyj.AutoWire.Person" id="person" autowire="byType">
<property name="name" value="王海"></property>
<property name="age" value="45"></property>
</bean>
<!--autowire="byName"表示自动检测该类中的setxxx方法,如果某个setxxx方法的x和容器某个对象的id对应,就会自动赋值给对应的属性-->
<bean class="com.hqyj.AutoWire.Person" id="person2" autowire="byName">
<property name="name" value="王海"></property>
<property name="age" value="45"></property>
</bean>
主方法:
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Person person = context.getBean("person", Person.class);
System.out.println(person);
context.close();
}
autowire属性的值
byType
类中要有被注入的属性的setter()方法
被自动注入的对象可以没有id
Spring容器中,某个对象的类型要与该setter()方法的参数类型-致,且容器中只有一个该类型的对象
如setCar(Car C), Spring就会自动在容器中寻找类型为Car的对象自动装配
byName
类中要有被注入的属性的setter()方法
被自动注入的对象必须要有id
实际是根据setXXX()方法set后的单词XXX关联
如setCar(Car c),Spring就会自动在容器中寻找id为car的对象自动装配
Spring核心注解
在spring配置文件中进行配置:
<!--如果要使用Spring注解,需要加入这段话,表示扫描指定包下所有使用了注解的类-->
<context:component-scan base-package="com.hqyj.Seconds.bookSystem"></context:component-scan>
BookDao类:
//这个注解相当于在配置文件中加入<bean class= "com.hqyj.Seconds.bookSystem.dao.BookDao"></bean>
@Component
publicclassBookDao {
publicvoidqueryAll(){
System.out.println("模拟查询所有图书信息");
}
}
BookService类
@Component//这个注解相当于在配置文件中加入<bean class= "com.hqyj.Seconds.bookSystem.service.BookServicel"></bean>
publicclassBookService {
@Autowired//这个注解相当于<bean class= "com.hqyj.Seconds.bookSystem.service.BookServicel"></bean>中加入autowire=byType
privateBookDaobookDao;
publicvoidshowAll(){
System.out.println("经过service执行dao中的方法");
bookDao.queryAll();
}
}
BookController类
//如果没有设置名称,获取当前类的对象,名称为类名的驼峰俞名法形式。如BookController, 获取时使用bookController获取
@Component
//这里的value表示给该bean设置id名,调用时使用controller
//@Component(value = "controller")
publicclassBookController {
@Autowired
privateBookServicebookService;
publicvoidshowAll(){
System.out.println("经过controller,调用service中的方法");
bookService.showAll();
}
}
Main方法
publicclassBookMain {
publicstaticvoidmain(String[] args) {
ClassPathXmlApplicationContextcontext=newClassPathXmlApplicationContext("book_system.xml");
//BookController类中使用@Component注解时使用
BookControllerbean=context.getBean("bookController",BookController.class);
//BookController类中使用@Component(value="controller")或者@Controller(value = "controller")注解时使用
//BookController bean = context.getBean("controller",BookController.class);
bean.showAll();
}
}
类上加的注解
@Component
当一个类不好归纳时,使用该注解定义为普通组件
@Controller
定义一个类为控制层组件
@Service
定义一个类为业务逻辑层组件
@Repository
定义一个类为持久层(数据访问层/dao层)组件
@Lazy/@Lazy(value=true)
设置该类为懒加载
@Scope/@Scope(valuye="singleton/prototype")
设置为单例/原型模式,默认单例模式
说明
以上注解的公共特点
都是将对应类的对象注入到Spring容器中,用于替换配置文件中的bean标签
默认是单例模式,只会创建一个对象,非懒加载,容器加载时会自动创建。
默认注入的对象id为当前类的类名首字母小写形式
如在BookDao类上添加@Component,id默认为bookDao
可以通过注解的value属性自定义注入的对象的id名,如@Component(value="key")表示注入的对象id为key
属性上加的注解
@Autowired
优先使用byType方式从Spring容器中获取对应类型的对象自动装配,先检索Spring容器中对应类型的数量。如果数量为0,直接报错;如果数量为1直接装配;如果有多个对应类型的对象,会尝试使用byName方式获取对应的id对象,但要配合@Qualifier(value=""某个对象的id)一起使用,指定id进行装配。
@Qualifier(value="某个对象的id")
配合@Autowired注解,使用byName方式获取某个对象id的bean进行装配
@Resource(name="某个对象的id")
该注解相当于@Autowired+@Qualifier(value="某个对象的id")
优先以byName的方式,从spring容器中检索出name为指定名的对象进行装配,如果没有则尝试byType方式,要求对象有且只有一个,否则也会报错。
说明
如果要在某个类中使用spring容器中的某个对象时,只需定义成员变量,无需创建对象,通过@Autowired或@Resource注解进行自动装配
实际开发中,绝大部分情况下,需要自动装配对象有且只有一个,并且命名规范,所以@Autowired或@Resource区别不是很大。@Autowired优先byType方式,@Resource优先使用byName方式。
如果@Resource不能使用,是因为缺少javax.annotation包,需要引入对应依赖
<!--pom.xml-->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</ dependency>
web项目中使用Spring
添加依赖
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!--spring容器-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<!--web集成-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.23</version>
</dependency>
在main目录下创建java和resources目录,修改web.xml版本为4.0
在resources目录下创建spring配置文件application.xml,扫描使用了注解的根包
<context:component-scan base-package="com.hqyj.springweb"></context:component-scan>
创建一个类,使用@Componet注解将其注入到Spring容器中
@Component
public class Pojo {
public void fun(){
System.out.println("hello springweb");
}
}
配置监听器用于初始化Spring容器
<!--配置全局监听器用于初始化Spring容器-->
<listener>
<!--ContextLoaderListener是一个全局监听器-->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--定义全局参数-->
<context-param>
<!--该监听器需要设置要读取的Spring配置文件路径-->
<param-name>contextConfigLocation</param-name>
<!--只是Spring配置文件的路径c1asspath:表示从根目录出发-->
<param-value>classpath:application.xml</param-value>
</context-param>
创建一个servlet,范围跟该servlet,获取spring容器,从容器中获取
@org.springframework.stereotype.Controller
@WebServlet("/hello")
public class MyController extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取Spring容器
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
//从容器中获取某个bean
Pojo pojo = context.getBean("pojo", Pojo.class);
pojo.fun();
}
}
如何初始化spring容器
实际就是如何在web应用中解析Spring的配置文件
在控制台应用程序中,可以在main方法中通过ClassPathXmlApplicationContext来解析application.xml文件, 初始化Spring容器。
在web项目中没有main方法,只有servlet中的service,如果在service方法中创建ClassPathXmlApplicationContext对象,会每次访问都执行
而Spring容器只需初始化一次,在项目启动时就解析application.xml文件,全局监听器就是一个很好的选择。
spring-web包中提供了一个ContextLoaderListener类, 它实现了ServletContextListener,属于项目级别的全局监听器。
这个类需要一个contextConfigLocation参数,表示要解析的application.xml文件的路径。
这个监听器会在项目启动时,读取指定的Spring配置文件路径,并且创建WebApplicationContext对象,即Spring容器, 这个对象保存在ServletContext中
在spring-web项目中使用Spring-jdbc
1.添加依赖
<dependencies>
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!--spring容器-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<!--web集成-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.23</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<!--jstl-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
2.创建包结构(controller、servlet、dao、entity),创建实体类
publicclassHero {
privateintid;
privateStringname;
privateStringposition;
privateStringsex;
private intprice;
privateStringshelfDate;
publicHero(intid, Stringname, Stringposition, Stringsex, intprice, StringshelfDate) {
this.id=id;
this.name=name;
this.position=position;
this.sex=sex;
this.price=price;
this.shelfDate=shelfDate;
}
publicHero(Stringname, Stringposition, Stringsex, intprice, StringshelfDate) {
this.name=name;
this.position=position;
this.sex=sex;
this.price=price;
this.shelfDate=shelfDate;
}
publicHero() {
}
//忽略setter/getter/toString方法
}
3.在spring配置文件中配置数据源和JDBC模板
<?xmlversion="1.0" encoding="UTF-8"?>
<beansxmlns="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:component-scanbase-package="com.hqyj.springweb"></context:component-scan>
<!--配置数据库-->
<beanclass="org.springframework.jdbc.datasource.DriverManagerDataSource"id="dataSource">
<!--配置连接驱动-->
<propertyname="driverClassName"value="com.mysql.cj.jdbc.Driver"></property>
<!--配置mysql地址-->
<propertyname="url"value="jdbc:mysql://localhost:3306/gamedb?serverTimezone=Asia/Shanghai"></property>
<propertyname="username"value="root"></property>
<propertyname="password"value="123456"></property>
</bean>
<!--配置JDBC模板-->
<beanclass="org.springframework.jdbc.core.JdbcTemplate"id="jdbcTemplate">
<!--设置要操作的数据源-->
<propertyname="dataSource"ref="dataSource"></property>
</bean>
</beans>
4.数据访问层(dao)的写法
@Repository
publicclassHeroDao {
@Autowired
//通过jdbc模板对象实现CRUD
privateJdbcTemplatejdbcTemplate;
publicList<Hero>queryAll() {
Stringsql="select * from hero";
//BeanPropertyRowMapper对象表示将查询到的字段与指定的类中的属性进行映射
BeanPropertyRowMapper<Hero>mapper=newBeanPropertyRowMapper<>(Hero.class);
//query()方法执行查询
List<Hero>list=jdbcTemplate.query(sql, mapper);
returnlist;
}
publicbooleandelete(intid) {
Stringsql="delete from hero where id=?";
inti=jdbcTemplate.update(sql, id);
returni>0;
}
publicbooleaninsert(Herohero){
Stringsql="insert into hero values(null,?,?,?,?,?)";
intinsert=jdbcTemplate.update(sql, hero.getName(), hero.getPosition(), hero.getSex(), hero.getPrice(), hero.getShelfDate());
returninsert>0;
}
publicHerofindById(intid){
Stringsql="select * from hero where id=?";
BeanPropertyRowMapper<Hero>mapper=newBeanPropertyRowMapper<>(Hero.class);
Herohero=jdbcTemplate.queryForObject(sql, mapper, id);
returnhero;
}
publicbooleanupdate(Herohero){
Stringsql="update hero set name=?,position=?,sex=?,price=?,shelf_date=? where id=?";
intupdate=jdbcTemplate.update(sql, hero.getName(), hero.getPosition(), hero.getSex(), hero.getPrice(), hero.getShelfDate(), hero.getId());
returnupdate>0;
}
}
5.控制层(controller)写法
@WebServlet("/hero")
publicclassHeroServletextendsHttpServlet {
@Override
protectedvoidservice(HttpServletRequestreq, HttpServletResponseresp) throwsServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//获取Spring容器,获取HeroDao对象
WebApplicationContextcontext=WebApplicationContextUtils.getWebApplicationContext(getServletContext());
HeroDaoheroDao=context.getBean("heroDao", HeroDao.class);
Stringop=req.getParameter("op");
switch (op) {
case"queryAll":
List<Hero>heroes=heroDao.queryAll();
req.setAttribute("list", heroes);
req.getRequestDispatcher("./pages/select.jsp").forward(req, resp);
System.out.println(heroes);
break;
case"delete":
Stringid=req.getParameter("id");
heroDao.delete(Integer.parseInt(id));
resp.sendRedirect("./hero?op=queryAll");
break;
case"insert":
Stringname=req.getParameter("name");
Stringposition=req.getParameter("position");
Stringsex=req.getParameter("sex");
intprice=Integer.parseInt(req.getParameter("price"));
Stringdate=req.getParameter("date");
Herohero=newHero(name, position, sex, price, date);
heroDao.insert(hero);
resp.sendRedirect("./hero?op=queryAll");
break;
case"findById":
intbyId=Integer.parseInt(req.getParameter("id"));
HerobyId1=heroDao.findById(byId);
req.setAttribute("hero",byId1);
req.getRequestDispatcher("./pages/update.jsp").forward(req, resp);
break;
case"update":
intupdateId=Integer.parseInt(req.getParameter("id"));
Stringname1=req.getParameter("name");
Stringposition1=req.getParameter("position");
Stringsex1=req.getParameter("sex");
intprice1=Integer.parseInt(req.getParameter("price"));
Stringdate1=req.getParameter("date");
Herohero1=newHero(updateId, name1, position1, sex1, price1, date1);
heroDao.update(hero1);
resp.sendRedirect("./hero?op=queryAll");
break;
}
}
}
6.JSP页面
<%@taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core" %>
<%@pagecontentType="text/html;charset=UTF-8"language="java" %>
<html>
<head>
<title>查询所有</title>
<style>
table {
border-collapse: collapse;
margin: 50pxauto;
}
td,th {
width: 100px;
height: 20px;
border: 1pxsolidblack;
}
td{
text-align: center;
}
</style>
</head>
<body>
<formaction="${pageContext.request.contextPath}/hero"method="post">
<inputtype="hidden"value="query"name="op">
<table>
<tr>
<th>编号</th>
<th>英雄名称</th>
<th>英雄位置</th>
<th>英雄性别</th>
<th>英雄价格</th>
<th>上架日期</th>
<thcolspan="2">操作</th>
</tr>
<c:forEachitems="${list}"var="hero">
<tr>
<td>${hero.id}</td>
<td>${hero.name}</td>
<td>${hero.position}</td>
<td>${hero.sex}</td>
<td>${hero.price}</td>
<td>${hero.shelfDate}</td>
<td><ahref="${pageContext.request.contextPath}/hero?op=delete&id=${hero.id}">删除</a></td>
<td><ahref="${pageContext.request.contextPath}/hero?op=findById&id=${hero.id}">修改</a></td>
</tr>
</c:forEach>
<tr>
<tdcolspan="8"><ahref="${pageContext.request.contextPath}/pages/insert.jsp">添加</a></td>
</tr>
</table>
</form>
</body>
</html>
<!--新增界面insert.jsp-->
<formaction="${pageContext.request.contextPath}/hero"method="post">
<inputtype="hidden"value="insert"name="op">
<table>
<tr>
<td>英雄角色</td>
<td><inputtype="text"name="name"></td>
</tr>
<tr>
<td>英雄位置</td>
<td><inputtype="text"name="position"></td>
</tr>
<tr>
<td>英雄性别</td>
<td><inputtype="text"name="sex"></td>
</tr>
<tr>
<td>英雄价格</td>
<td><inputtype="number"name="price"></td>
</tr>
<tr>
<td>上架日期</td>
<td><inputtype="date"name="date"></td>
</tr>
<tr>
<td><inputtype="submit"value="添加"></td>
</tr>
</table>
</form>
<!--修改界面update.jsp-->
<formaction="${pageContext.request.contextPath}/hero"method="post">
<inputtype="hidden"value="update"name="op">
<inputtype="hidden"name="id"value="${hero.id}">
<table>
<tr>
<td>英雄角色</td>
<td><inputtype="text"name="name"value="${hero.name}"></td>
</tr>
<tr>
<td>英雄位置</td>
<td><inputtype="text"name="position"value="${hero.position}"></td>
</tr>
<tr>
<td>英雄性别</td>
<td><inputtype="text"name="sex"value="${hero.sex}"></td>
</tr>
<tr>
<td>英雄价格</td>
<td><inputtype="number"name="price"value="${hero.price}"></td>
</tr>
<tr>
<td>上架日期</td>
<td><inputtype="date"name="date"value="${hero.shelfDate}"></td>
</tr>
<tr>
<td><inputtype="submit"value="修改"></td>
</tr>
</table>
</form>
JDBCTemplate常用方法
方法 | 作用 | 说明 |
query(String sgl, RowMapper mapper) | 无条件查询 | 返回值为List集合 |
update(String sql) | 无条件更新(删除、修改) | 返回值是受影响的行数 |
query(String sgl, RowMapper mapper,Object... objs) | 条件查询 | 可变参数为?的值 |
update(String sql,Object...objs) | 条件更新(添加,删除,修改) | 可变参数为?的值 |
gueryForObject(String sgl, RowMapper mapper) | 无条件查询单个对象 | 返回值是指定对象 |
gueryForObject(String sgl, RowMapper mapper,Object...objs) | 条件查询单个对象 | 返回值是指定对象 |
execute(String sql) | 执行指定的sql | 无返回值 |
SpringMVC
在web阶段中,控制层是由servlet实现,传统的Servlet,需要创建、重写方法、配置映射。使用时极不方便,SpringMVC可以替换Servlet。
SpringMVC是Spring框架中位于Web开发中的一个模块, 是Spring基于MVC设计模式设计的轻量级Web框架。
使用SpringMVC
1.创建webapp项目
修改web.xml版本为4.0
创建Java和resources目录
创建包结构
2.添加依赖
<!--spring-webmvc-->
<!--这个依赖会包含spring-web和spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.23</version>
</dependency>
3.配置初始化spring
在resources目录下创建spring配置文件application.xml
<?xmlversion="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:mvc="http://www.springframework.org/schema/mvc"
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/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--设置扫描使用了注解的根包-->
<context:component-scanbase-package="com.hqyj.springmvc"></context:component-scan>
</beans>
在web.xml中使用监听器初始化
<?xmlversion="1.0" encoding="UTF-8"?>
<web-appxmlns="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>
<listener-class>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>
4.配置springMVC
在resources目录下创建配置SpringMVC的配置文件springmvc.xm
配置要扫描的控制层类所在的包名
配置内部资源视图解析器以及视图路径的前后缀
<?xmlversion="1.0" encoding="UTF-8"?>
<beansxmlns="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:component-scanbase-package="com.hqyj.springmvc.controller"></context:component-scan>
<!--配置内部资源视图解析器-->
<beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--最终控制层跳转的页面所在的路径及页面自身后缀名-->
<!--jsp页面不建议直接通过浏览器访问。在EWEB-INF目录下的资源,无法通过浏览器直接访问,所以将jsp保存在WEB-INF目录下,最好创建一个pages-->
<propertyname="prefix"value="/WEB-INF/pages/"></property>
<!--现阶段使用jsp输出数据,所以后缀名为.jsp-->
<propertyname="suffix"value=".jsp"></property>
</bean>
</beans>
在web.xml中配置DispatcherServlet
将该Servlet的请求映射设置为/,表示所有请求都会访问该Servlet,由该Servlet再进行分发
<!--配置DispatcherServlet-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--设置该Servlet的初始化参数,用于读取SrpingMVC配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--设置该serlvet的映射为/或*.do-->
<url-pattern>/</url-pattern>
</servlet-mapping>
5.在WEB-INF目录下创建一个pages目录,在其中创建一个welcome.jsp页面
通常jsp页面不允许被浏览器直接访问,需要保存在WEB-INF目录下
6.编写控制层代码
在controller包下创建一个类,加上@controller注解
方法加入@RequestMapping()注解表示访问该方法的映射
该类中定义有的方法返回值通常为字符串,表示某个页面的名称,也可以是另一个controller中的方法映射名
//加入@Controller注解表示该类是一个控制层类,替换之前的servlet
@Controller
//该注解如果只设置请求映射,直接填字符申
@RequestMapping(path="/first")
publicclassHelloController {
//该注解如果还有其他参数要设置,路径用path赋值
@RequestMapping(path="/hello")
publicStringhello(){
//返回的字符申是某个页面的名称或另一个控制层中方法的请求映射
return"welcome";//welcome是jsp页面名
}
}
SpringMVC相关注解
@Controller
只能写在类上,表示该类属于一个控制器
@RequestMapping("/请求映射名")/@RequestMapping(value="/请求映射名")/@RequestMapping(path="/请求映射名")
该注解可以写在类或方法上,写在类上通常用于区分功能模块,写在类上用于区分具体功能
默认写一个属性或value或path后的值,都表示访问该类或该方法时的请求映射
RequestMapping(value="/请求映射名",method=RequestMethod.GET/post/put/delete)
method属性表示使用哪种请求方式访问该类或该方法
如果注解中不止一个属性,每个属性都需要指定属性名
@GetMapping("/请求映射名")相当于@RequestMapping(value="/请求映射名",method=RequestMethod.GET)
post、put、delete同理
@GetMapping只能写在方法上
@PathVariable
该注解写在某个方法的某个形参上
通常用于跳转指定页面
通常配合@RequestMapping("/{path}")获取请求时传递的参数
@RequestMapping("/{path}")
publicStringfun(@PathVariable("path") StringpageName){
}
//当前方法如果通过localhost:8080/项目名/he11o访问,就会跳转到hello.jsp
//当前方法如果通过localhost: 8080/项目名/error访问,就会跳转到error .jsp
//映射中的/{path}就是获取路径中的hello或error,将其赋值给形参
@RequestParam(value="传递的参数名",defaultValue="没有传递参数时的默认值")
该注解写在某个方法的某个参数上
用于获取提交的数据,可以设置默认值在没有提交数据时使用
解决提交数据时的中文乱码
使用过滤器解决中文乱码。
在web.xml中配置spring-web提供的CharaterEncodingFilter过滤器
<!--定文解决中文乱码的过器CharacterEncodingFilter-->
<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>
将数据保存到作用域中
在controller中的某个方法的参数列表里添加Model参数
调用Model对象的addAttribute(String str,Object obj)方法, 将某个obj对象保存在request作用域中, 命名为str
//将数据保存到作用域中
@RequestMapping("/queryAll")
public String queryAll(Model model){
ArrayList list = new ArrayList<>();
list.add("adsh ");
list.add(1212);
list.add(true);
list.add("米西米西");
//将List保存到请求中
model.addAttribute("list",list);
return "welcome";
}
在controller中的某个方法的参数列表中添加ModelAndView参数,同时将该方法的返回值设置为ModelAndView类型。
使用ModelAndView对象调用addObject(String str,Object obj),将某个obj对象保存在request作用域中,命名为str
使用ModelAndView对象调用setViewName(String viewName),跳转到viewName页面
@Controller
@RequestMapping(path = "/hero")
//如果要将数据保存到session中,使用该注解定义session中对象的名称
@SessionAttributes({"str1","str2"})
public class HeroController {
@Autowired
private HeroService heroService;
@RequestMapping("/queryAll")
public String queryAll(Model model) {
List<Hero> heroes = heroService.queryAll();
model.addAttribute("list", heroes);
return "heroList";
}
@RequestMapping("/showAll")
public ModelAndView queryAll(ModelAndView mav) {
List<Hero> heroList = heroService.queryAll();
//保存查询出的对象到请求中,命名为list,默认保存在request作用域中
mav.addObject("list", heroList);
// mav.addObject("str1", heroList);//保存在session中
//设置要跳转的页面
mav.setViewName("heroList");
return mav;
}
}
数据都是保存在request作用域中,在页面中使用JSP内置对象或EL获取
如果要保存到session中,需要在类上加入@SessionAttributes({"str1","str2"})注解,str1,str2表示保存到session中的对象的名称,再在方法中,使用Model对象的addAttribute(String str,Object obj)将其保存在session中
SpringMVC中的跳转
控制层跳转到某个jsp页面
在控制层中定义方法,这种方式跳转属于请求转发
如果要使用重定向,在页面名之前添加"redirect:"
@RequestMapping("/he11o")
public String hello(){
return "hello";//hello是一个界面,转发
return "redirect:hello";//重定向
}
//第二种
@RequestMapping("/he11o")
public ModelAndView hello(ModelAndView mav){
mav.setViewName("hello");
return mav;
}
在springmvc配置文件中
<mvc:view-controller path="请求映射" view-name="页面名称"></mvc:view-controller>
<!--设置请求视图映射,跳转到一个页面-->
<!--该标签无法@RequestMapping共存-->
<mvc:view-controller path="/" view-name="welcome"></mvc:view-controller>
<!--如果要同时使用@Reques tMapping和mvc :view-controller标签,需要加入以下标签,该标签位于xmIns :mvc= "http://www. springframework. org/schema/mvc 1-->
<mvc:annotation-driven></mvc:annotation-driven>
控制层跳转到另一个控制层中
方法的返回值为"redirect/forward:另一 个控制层中方法的映射名"
@RequestMapping("/he11o")
public String hello(){
return "forword:hello";//使用请求转发的方式,跳转到映射名为hero的控制层
return "redirect:hello";//使用重定向的方式,跳转到映射名为hero的控制层
}
JSP页面跳转另一个JSP页面
当前项目中JSP页面搜在WEB-INF目录下,无法直接访问,a标签同样如此,只能通过controller控制层跳转页面
@Controller
public class ToPageController {
@RequestMapping("/")
public String toIndex(){
return "redirect:/hero/queryAll";
}
// 这个方法的作用:会将请求中第一个/后的单词截取出来命名为ath赋值给参数page
//如localhost:8080/web/hero, 就会识别出hero,return "hero";
//通过视图解析器,就会跳转到/WEB-INF/pages/hero.jsp页面
@RequestMapping("/{path}")
public String toPage(@PathVariable("path") String page){
return page;
}
}
这时在页面中这样跳转
<%--SpringMVC中如果要跳转页面,必须途径controller,不能使用a标签跳转--%>
<%--这个路径实际是/项目名/addHero,会截取addHero,跳转到addHero. jsp--%>
<a href="${pageContext.request.contextPath}/addHero">添加</a>
文件上传
使用apche提供的通用文件上传组件实现
1.导入所需依赖
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
2.向spring容器中注入上传文件的核心类
<!--注入上传文件的核心类:通用多部件解析器CommonsMultipartResolver-->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<!--设置上传的单个文件最大字节:10MB=1024*1024*10-->
<property name="maxUploadSizePerFile" value="10485760"></property>
</bean>
3.上传页面的表单
<!--.上传的表单控件使用file-->
<!--提交方式只能为post-->
<!--添tenctype= "multipart/form-data"属性-->
<form method="post" enctype="multipart/form-data" action="./admin/upload">
请选择图片<input type="file" name="uploadFile">
<input type="submit" value="上传">
</form>
4.控制层获取上传的文件
@Controller
@RequestMapping("/admin")
public class UploadController {
@RequestMapping("/upload")
public String upload(MultipartFile uploadFile) throws IOException {//uploadFile就是上传的文件对象
//获取上传的文件名
String oldName = uploadFile.getOriginalFilename();
//得到源文件的后缀名
String prefix = oldName.substring(oldName.lastIndexOf("."));
//有可能不同的人上传的文件名相同,所以获取源文件的后缴后,生成一个随机文件名, 拼接新文件名。
String newName = UUID.randomUUID() + prefix;
//创建一个文件File对象
File file = new File("D:\\idea上传图片",newName);
System.out.println(file);
//将文件写入硬盘中
uploadFile.transferTo(file);
return "welcome";
}
}
配置Spring+SpringMVC时用到的关键类
spring全局监听器(web.xml)
ContextLoaderListener
配置springMVC内部资源视图解析器(在Springmvc配置文件中)
InternalResourceViewResolver
配置springMVC请求分发Servlet(web.xml)
DispatcherServlet
字符编码格式过滤器(web.xml)
CharacterEncodingFilter
MyBatis
一个半自动化的ORM框架。原本叫做ibatis, 后来升级改名为MyBatis。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
使用XML文件或注解的形式,将SQL语句映射为持久层(dao层)中的方法
特点
方便
简化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
mysql
mybatis-spring
druid
spring-jdbc
jstl
4.配置Spring
创建application.xml
扫描项目根包
配置web.xml
设置全局参数:<context-param>,读取application.xml文件
设置全局监听器:ContextLoaderListener,初始化spring容器
5.配置SpringMVC
创建springmvc.xml
扫描控制层所在包
设置内部资源视图解析器:InternalResourceViewResolver,设置前后缀
配置web.xml
设置请求分发器:DispatcherServlet,在其中读取springmvc.xml配置文件
过滤器解决请求中文乱码:CharacterEncodingFilter
6.配置MyBatis
创建mybatis-config.xml(官网模板)
在application.xml中注入
数据库连接池Druid:DruidDaJaSource
SQL会话工厂:SqISessionFactoryBean
映射扫描配置器: MapperScannerConfigurer
7.执行sql语句
创建dao层接口,定义方法
在resoureces下创建mapper目录,创建sql映射文件xx.xml(官网模板),在其中定义sql语句
实现步骤
1.创建基于Maven的webapp项目
2.修改web.xml版本为4.0,创建Java、resoureces目录、项目包目录、web-inf下的页面目录
3.导入依赖
<dependencies>
<!--jstl-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--springWebMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.23</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>
<!--spring集成JDBC-->
<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.31</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
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">
<!--扫描使用了SpringMVC注解的根包-->
<context:component-scan base-package="com.hqyj.ssm"></context:component-scan>
</beans>
在web.xml中配置监听器和全局参数
<?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>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
</web-app>
5.配置SpringMVC
在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">
<!--扫描使用了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和中文乱码过滤器
<?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>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<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中注入
<?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.ssm"></context:component-scan>
<!--注入Druid数据源-->
<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/gamedb?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:mybatis-config.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.ssm.dao"></property>
</bean>
</beans>
7.执行SQL
package com.hqyj.ssm.entity;
public class Hero {
private int id;
private String name;
private String position;
private String sex;
private int price;
private String shelf_date;
@Override
public String toString() {
return "{编号:" + id + ",名称:" + name + ",位置:" + position + ",性别:" + sex + ",价格:" + price + ",上架日期:" + shelf_date + "}";
}
//省略了setter/getter方法
dao层接口
//使用MyBatis时,数据访问层/持久层/dao层,只需定义为接口
@Repository
public interface HeroDao {
//查询所有
List<Hero> queryAll();
}
service层
@Service
public class HeroService {
@Autowired
private HeroDao heroDao;
public List<Hero> queryAll(){
return heroDao.queryAll();
}
}
controller层
@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);
System.out.println(list);
return "heroList";
}
//和queryAll作用一致
/*public ModelAndView queryAll(ModelAndView mac) {
List<Hero> list = heroService.queryAll();
mac.addObject("list",list);
mac.setViewName("heroList");
System.out.println(list);
return mac;
}*/
}
在resources下的mapper目录下,创建sql映射文件,命名通常为"实体Mapper",如HeroMapper.xml
<?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.ssm.dao.HeroDao">
<!--查询select标签-->
<!--id对应dao层中的某个方法名-->
<!--如果实体类中的属性名和表中的字段名一致,加入resultType属性,设置实体的全限定名-->
<select id="queryAll" resultType="com.hqyj.ssm.entity.Hero">
select * from hero
</select>
<!--表中字段和实体类中字段不同使用下列方法-->
<!--该标签用于给指定的实体类设置返回结果集映射-->
<!--id属性自定义该映射名称-->
<!--id属性自定义该映射名称-->
<!--type属性指定实体类-->
<!-- <resultMap id="heroMap" type="com.hqyj.ssm.entity.Hero">-->
<!--主键字段用id标签,其他字段用result标签-->
<!--id和result标签都有两个属性property表示实体类中的属性名,column表示表中的字段名-->
<!-- <id property="id" column="id"></id>-->
<!-- <result column="price" property="jiage"></result>-->
<!--如果遇到下划线转驼峰命名,无需在这里自定义映射,可以在mybatis 的配置文件中设置开启驼峰命名映外-->
<!-- </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;
margin: 30px auto;
}
td, th {
width: 130px;
height: 30px;
border: 1px solid darkorange;
text-align: center;
}
</style>
</head>
<body>
<table>
<tr>
<th>英雄编号</th>
<th>英雄名称</th>
<th>英雄位置</th>
<th>英雄性别</th>
<th>英雄价格</th>
<th>上架日期</th>
</tr>
<c:forEach items="${list}" var="hero">
<tr>
<td>${hero.id}</td>
<td>${hero.name}</td>
<td>${hero.position}</td>
<td>${hero.sex}</td>
<td>${hero.price}</td>
<td>${hero.shelf_date}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
补充
通过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=123456
在application.xml中读取properties文件
<!--读取properties文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
读取时使用EL表达式访问其中的键
<!--Druid数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<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基本增删改查
实体类:
package com.hqyj.ssm2.entity;
import org.omg.CORBA.PRIVATE_MEMBER;
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;
//用于修改类型、价格和库存
public BookInfo(int bookId, int typeId, int bookPrice, int bookNum) {
this.bookId = bookId;
TypeId = typeId;
this.bookPrice = bookPrice;
this.bookNum = bookNum;
}
//用于全部修改
public BookInfo(int bookId, int typeId, String bookName, String bookAuthor, int bookPrice, int bookNum, String publisherDate, String bookImg) {
this.bookId = bookId;
TypeId = typeId;
this.bookName = bookName;
this.bookAuthor = bookAuthor;
this.bookPrice = bookPrice;
this.bookNum = bookNum;
this.publisherDate = publisherDate;
this.bookImg = bookImg;
}
public BookInfo() {
}
//忽略setter/getter/toString方法
}
dao层
@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人中某个方法中有多个参数,需要给任个多数添加@Param("参数名")注解,给该参数命名
//命名后,才能在mybatis的sql映射文件中使用该参数,即#{参数行}
//如这里的newPrice,在sql中#{newPrice}获取
//只修改类型、价格和库存
int update(@Param("newTypeId") int typeId,@Param("newPrice") int bookPrice, @Param("newNum") int bookNum, @Param("updateId") int bookId);
}
service层
@Service
public class BookInfoService {
@Autowired
private BookInfoDao bookInfoDao;
//查询所有
public List<BookInfo> queryAll(){
return bookInfoDao.queryAll();
}
//根据id删除
public boolean delete(int id){
return bookInfoDao.delete(id)>0;
}
//添加
public boolean insert(BookInfo bookInfo){
return bookInfoDao.insert(bookInfo)>0;
}
//根据id查找
public BookInfo findById(int no){
return bookInfoDao.findById(no);
}
//修改全部
/*public boolean update(BookInfo bookInfo){
return bookInfoDao.update(bookInfo)>0;
}*/
//只修改类型、价格和库存
public boolean update(int typeId,int bookPrice,int bookNum,int bookId){
return bookInfoDao.update(typeId, bookPrice, bookNum, bookId)>0;
}
}
controller层
@Controller
@RequestMapping("/bookInfo")
public class BookInfoController {
@Autowired
private BookInfoService bookInfoService;
//注入BookTypeService,用于获取所有的图书类型
@Autowired
private BookTypeService bookTypeService;
//查询所有
@RequestMapping("/queryAll")
public String queryAll(Model model) {
List<BookInfo> list = bookInfoService.queryAll();
model.addAttribute("list", list);
return "bookInfoList";
}
//根据id删除
@RequestMapping("/delete")
public String delete(int id) {
if (bookInfoService.delete(id)) {
return "redirect:queryAll";
}
return "error";
}
//添加
@RequestMapping("/insert")
//如果表单提交的参数那方法的形参名一致, 自动获取并赋值
//如果表单提交的所有参数正好是一个实体类对象,可以用对应的实体类对象获取
public String insert(BookInfo bookInfo) {
// System.out.println(bookInfo);
if (bookInfoService.insert(bookInfo)) {
return "redirect:queryAll";
}
return "error";
}
@RequestMapping("/findById")
public String findById(@RequestParam("id") int no, Model model){
//查询对应的图书信息
BookInfo byId = bookInfoService.findById(no);
model.addAttribute("book",byId);
//查询所有图书的类型
List<BookType> bookTypes = bookTypeService.queryAll();
model.addAttribute("btList",bookTypes);
return "bookEdit";
}
//全部都可以修改
/*@RequestMapping("/update")
public String update(BookInfo bookInfo){
if (bookInfoService.update(bookInfo)) {
return "redirect:queryAll";
}
return "error";
}*/
@RequestMapping("/update")
public String update(int typeId,int bookPrice,int bookNum,int bookId){
if (bookInfoService.update(typeId, bookPrice, bookNum, bookId)) {
return "redirect:queryAll";
}
return "error";
}
}
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">
<mapper namespace="com.hqyj.ssm2.dao.BookInfoDao">
<select id="queryAll" resultType="com.hqyj.ssm2.entity.BookInfo">
select * from book_info
</select>
<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>
<select id="findById" resultType="com.hqyj.ssm2.entity.BookInfo">
select * from book_info where book_id=#{no}
</select>
<!--全部都可以修改-->
<!--<update id="update">
update book_info set
type_id=#{typeId},book_name=#{bookName},book_author=#{bookAuthor},book_price=#{bookPrice},book_num=#{bookNum},publisher_date=#{publisherDate},book_img=#{bookImg}
where book_id=#{bookId}
</update>-->
<!--只修改类型、库存、价格-->
<update id="update">
update book_info set type_id=#{newTypeId},book_price=#{newPrice},book_num=#{newNum} where book_id=#{updateId}
</update>
</mapper>
SpringBoot
特点
内置了Tomcat服务器,不需要部署项目到Tomcat中
内置了数据源Hikair
减少了jar文件依赖的配置
SpringBoot中的配置文件可以使用yml格式文件,代替xml
创建SpringBoot项目并选择web依赖
依赖可以创建项目时添加,也可以创建好项目之后添加
热部署和Lombok依赖
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.7.8</version>
</dependency>
<!--Lombox-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
在某个类上添加注解
lombok常用注解 | 作用 |
@AllArgsConstructor | 自动生成全参构造方法 |
@Data | 该注解相当于一下注解的和 |
@NoArgsConstructor | 自动生成无参构造方法 |
@Setter | 生成setter方法 |
@Getter | 生成getter方法 |
@ToString | 生成toString方法 |
@EqualsAndHashCode | 自动生成hashcode以及equals方法 |
SpringBoot+MyBatis实现单表查询
1.创建好SpringBoot项目
最好在创建的时候选择以下依赖
spring-web(必选)
lombok
spring-devtools
springboot集成mybatis
mysql驱动
2.在pom.xml中添加mybatis集成SpringBoot依赖和数据库驱动
<!--springboot集成mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
3.在springboot配置文件application.properties中
#properties文件称为属性文件,数据以键值对的形式保存 键=值
#项目端口号
server.port=8088
#项目上下文路径:http://localhost:8088/springboot/
server.servlet.context-path=/springboot
#mybatis配置
#开启驼峰映射
mybatis.configuration.map-underscore-to-camel-case=true
#sql日志:打印sql语句
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#扫描mybatis的sql映射文件
mybatis.mapper-locations=classpath:mapper/*.xml
#数据库信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/gamedb?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
mybatis的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">
<!--设置该文件对应的dao层接口全限定名-->
<mapper namespace="com.hqyj.springboot.mapper.HeroMapper">
</mapper>
4.根据数据表创建实体类、mapper(dao)层接口、service、controller
表(Hero):id(编号) name(名称) position(位置) sex(性别) price(价格) shelf_date(上架日期)
实体类
//该注解会自动生成getter/setter/toString/equals/hashCode方法
@Data
public class Hero {
private Integer id;
private String name;
private String position;
private String sex;
private Integer price;
private String shelfDate;
}
mapper层接口
@Repository
public interface HeroMapper {
List<Hero> queryAll();
}
mapper.xml
<?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">
<mapper namespace="com.hqyj.springboot.mapper.HeroMapper">
<select id="queryAll" resultType="com.hqyj.springboot.entity.Hero">
select * from hero
</select>
</mapper>
service层
@Service
public class HeroService{
@Autowired
private HeroMapper heroMapper;
public List<Hero> queryAll(){
return heroMapper.queryAll();
}
}
controller层
@Controller
@RequestMapping("/hero")
public class HeroController {
@Autowired
private HeroService heroService;
@RequestMapping("/queryAll")
@ResponseBody
public List<Hero> queryAll(){
return heroService.queryAll();
}
}
5.在SpringBoot的启动类上加入@MapperScan注解,去扫描mapper层所在目录
@SpringBootApplication
//扫描mapper(dao)层所在包
@MapperScan("com.hqyj.springboot.mapper")
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
MyBatisPlus
MyBatis-Plus (简称MP)是一个MyBatis的增强工具, 在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。
特点
只需简单的配置,就能实现对单表的CURD。
其核心有两个接口:BaseMapper和IService
BaseMapper中封装了大量数据访问层的方法
Iservice中封装了大量业务流程层的方法
SpringBoot+MyBatisPlus
1.创建SpringBoot项目
创建时添加依赖
devtools
lombok
spring-web
mysql Driver
2.导入SpringBoot集成MyBatisPlus依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
3.配置application.properties文件
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/gamedb?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
# 开启sql日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 无需加入开启驼峰命名映射,MyBatisPlus默认使用驼峰命名进行属性字段映射
# mybatis-plus.configuration.map-underscore-to-camel-case=true
4.根据数据库创建实体类
实体类的属性名命名方式:
MyBatisPlus默认使用驼峰命名对字段和属性进行映射,所以将字段stu_name对应的属性写为stuName
如果字段名和属性名不一致,在属性名上加入@TableField(value="字段名")
主键字段对应的属性,需要加入@Tableld注解,在数据库中也要将主键设置为自增
@Tableld(type = ldType.AUTO)表示主键自增,在数据库也要将主键设置为自增
@TableId(type = IdType.ASSIGN_ID)表示使用"雪花算法"(根据时间和机器特征码)生成一个id
@TableId(type = IdType.ASSIGN_UUID)表示使用UUID生成一个随机字符串id
@Data
public class Hero {
private Integer id;
//如果属性和字段名不一致,需添加下列注解
@TableField(value = "name")
private String heroName;
private String position;
private String sex;
private Integer price;
private String shelfDate;
}
5.编写数据访问层接口
可以不用写@Repository,继承BaseMapper接口,设置泛型
package com.hqyj.sbmp1.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hqyj.sbmp1.entity.Hero;
/*数据访问层可以称为dao层或mapper层
*可以不用加@Repository注解
* */
public interface HeroMapper extends BaseMapper<Hero> {
}
6.在SpringBoot启动类中,扫描数据访问层所在包
package com.hqyj.sbmp1;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.hqyj.sbmp1.mapper")
public class Sbmp1Application {
public static void main(String[] args) {
SpringApplication.run(Sbmp1Application.class, args);
}
}
BashMapper接口
BashMapper接口中定义了常用的增删改查方法,通常在数据访问层接口中继承该接口,就能直接使用其中所有方法
使用
package com.hqyj.sbmp1.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hqyj.sbmp1.entity.Hero;
/*数据访问层可以称为dao层或mapper层
*可以不用加@Repository注解
* */
public interface HeroMapper extends BaseMapper<Hero> {
}
BashMapper接口中的常用方法
方法名 | 参数 | 作用 |
selectList(Wrapper wrapper) | 条件构造器 | 条件查询集合,如果实参为null表示查询所有,返回List集合 |
selectById(Serializable id) | 主键 | 根据主键查询单个对象,返回单个对象 |
selectByOne(Wrapper wrapper) | 条件构造器 | 条件查询单个对象,返回单个对象 |
insert(T entity) | 实体对象 | 添加 |
updateById(T entity) | 实体对象 | 根据实体对象单个修改,对象必须至少有一个属性 |
update(T entity,Wrapper wrapper) | 实体对象和条件构造器 | 根据条件修改全部,对象必须至少有一个属性 |
deleteById(Serializable id/T entity) | 主键/实体对象 | 根据主键删除单个对象 |
deleteBatchlds(Collection col) | 主键集合 | 根据集合删除 |
delete(Wrapper wrapper) | 条件构造器 | 根据条件删除 |
IService接口
为了减少业务逻辑层的代码,并对BaseMapper进行拓展
通常在业务流程层中继承该接口,就能直接使用其中的所有方法
使用
1.创建一个业务层接口,继承Iservice接口,设置泛型
//创建业务逻辑层接口,继承IService<T>接口,设置泛型
public interface HeroService extends IService<Hero> {
}
2.创建一个业务层接口的实现类,添加@Service注解,继承ServiceImpl<M,T>,实现上一步创建的接口
M是数据访问层接口
T是实体类
/*1.添加Service
*2.继承ServiceImpl<M,T> M是Mapper类 T是实体类
*3.实现自定义Service接口
* */
@Service
public class ImpHeroService extends ServiceImpl<HeroMapper, Hero> implements HeroService {
}
IService接口中的常用方法
方法 | 作用 |
list() | 无条件查询所有 |
list(Wrapper wrapper) | 条件查询所有 |
page(Page page) | 无条件分页查询,Page是分页模型对象 |
page(Page page,Wrapper wrapper) | 条件分页查询,Page是分页模型对象 |
getById(Serializable id) | 根据主键查询单个对象 |
getOne(Wrapper wrapper) | 根据条件查询,如果参数为null,表示查询所有,会导致异常 |
save(T entity) | 添加 |
updateById(T entity) | 修改,参数至少有一个属性值和主键 |
saveOrUpdate(T entity) | 添加或修改,如果实参对象的主键值不存在则添加,存在则修改 |
update(T entity,Wrapper wrapper) | 条件修改,条件为null则修改全部数据 |
removeById(Serializable id/T entity) | 根据主键或包含主键的对象删除 |
removeBatchByIds(Collection ids) | 根据集合删除 |
remove(Wrapper wrapper) | 根据条件删除,条件为null则删除全部 |
条件构造器Wrapper
BaseMapper和IService接口中有很多方法都有这个参数,表示一个条件构造器对象。
如果该参数实际传递的值为nll,表示没有任何条件
这个Wrapper是一个抽象类,如果想要带条件,就要创建一个该类的子类对象。
常用子类为QueryWrapper和UpdateWrapper,
查询是创建QueryWrapper对象,更新时创建UpdateWrapper,实际使用无区别。
Wrapper对象带参数
Wrapper wrapper = new QueryWrapper<>(T entity);
Wrapper构造方法的参数如果是一个实体对象,只要该对象的属性不为空,就会将所有属性用and拼接起来作为条件
这种适合已知某个字段为某个值时使用。
@Test
void test1(){
//创建实体对象,只带两个属性值
Hero hero = new Hero();
hero.setHeroName("杨戬");
hero.setPosition("战士");
//创建一个带实体参数的条件构造器对象
Wrapper<Hero> wrapper = new QueryWrapper<>(hero);
System.out.println(heroService.getOne(wrapper));
}
Wrapper对象不带参数
Wrapper wrapper = new QueryWrapper<>();
当条件构造器不带参数时,就需要自定义条件。
这种适用于自定义查询条件
默认多个条件时,用and拼接
指定值或范围
//指定值或范围
@Test
void test2(){
/*
*eq(String column,Object val) equals column=val
*ne(String column,Object val) not equals column<>val
*lt(String column,Object val) less then column<val
*gt(String column,Object val) great then column>val
*le(String column,Object val) less equals column<=val
*ge(String column,Object val) great equals column>=val
*between(String column,Object val1,0bject vol2) between and column between val1 and vol2
*notBetween(String column,Object val1,0bject vol2) not between and column not between val1 and vol2
* */
//创建一个不带实体参数的条件构造器对象
QueryWrapper<Hero> wrapper = new QueryWrapper<>();
//自定义条件:指定值 查询性别为男的数据
wrapper.eq("sex","男");
//查询性别不为男的数据
// wrapper.ne("sex","男");
//查询价格在10000-19000的记录
wrapper.between("price",10000,19000);
//查询价格小于18888的战士,多个条件默认用and拼接
wrapper.lt("price",18888);
wrapper.eq("position","战士");
//查询性别为女或价格大于8000
//or()方法会将前后的条件使用or拼接
wrapper.gt("price",8000);
wrapper.or();
wrapper.eq("sex","女");
//查询性别为男的战士或价格大于13000的法师
wrapper.eq("sex","男");
wrapper.eq("position","战士");
wrapper.or();
wrapper.eq("position","法师");
wrapper.gt("price",13000);
//采用链式写法
wrapper.eq("sex","男")
.eq("position","战士")
.or()
.eq("position","法师")
.gt("price",13000);
heroService.list(wrapper).forEach(System.out::println);
}
空值和模糊查询
//空值
@Test
void test3(){
/*
*isNulL(String column) column is null
* isNotNull(String column) column is not null
* like(String column, 0bject vol) column like '%val%'
* likeLeft(String column, 0bject vol) column like '%val'
* likeRight(String column, 0bject vol) column like 'val%'
* notLike(String column, 0bject vol) column not like '%val%'
* notLikeLeft(String column, 0bject vol) column not like '%val'
* notLikeRight(String column, 0bject vol) column not like 'val%'
* */
QueryWrapper<Hero> wrapper = new QueryWrapper<>();
//字段为空
wrapper.isNull("shelf_date");
//带有指定关键字
wrapper.like("name","孙");
//以指定关键字结尾
wrapper.likeLeft("name","空");
//以指定关键字开头
wrapper.likeRight("name","孙");
//查询名字中带有孙字且上架时间为空的数据
wrapper.like("name","孙");
wrapper.isNull("shelf_date");
heroService.list(wrapper).forEach(System.out::println);
}
集合和排序
/*集合和排序*/
@Test
void test4(){
/*集合
*in(String column, 0bject... vals) column in (val1, val2...)
*in(String column, Collection vals) column in (val1, val2...)
* notIn(String column, 0bject... vals) column not in (val1, val2...)
*notIn(String column, Collection vals) column not in (val1, val2...)
*
* 排序
* orderByAsc(String column) order by column asC
* orderByDesc(String column) order by coLumn desc
* */
QueryWrapper<Hero> wrapper = new QueryWrapper<>();
//职业为战士或法师或射手
wrapper.eq("position","战士").or().eq("position","法师").or().eq("position","射手");
wrapper.in("position","战士","法师","射手");
//集合
wrapper.in("position", Arrays.asList("战士","法师","射手"));
//排序
//所有男性角色,按价格降序
wrapper.eq("sex","男").orderByDesc("price");
heroService.list(wrapper).forEach(System.out::println);
}
分组聚合
分组通常会和聚合函数(count,sum,max,min,avg)一起使用,但MyBatisPlus中没有现有的聚合函数。
需要使用select(String...Fields)自定义查询字段
@Test
void test5(){
/*
* select(String... fields) select field1,field2...
* groupBy(String column) group by column
* */
QueryWrapper<Hero> wrapper = new QueryWrapper<>();
//无参数时,查询所有字段
wrapper.select();
//查询指定字段
wrapper.select("name");
//使用聚合函数统计数量
wrapper.select("count(id)");
//查询不同性别的人数
//sql语句:select sex,count(id) from hero group by sex
wrapper.select("sex","count(id)");
wrapper.groupBy("sex");//根据指定字段分组
//根据定位分组,查询值为法师、战士和射手的人数,按人数降序
//select position,count(id) from hero where position in('法师','战士','射手') group by position order by count(id) desc
wrapper.select("position","count(id)")
.in("position","法师","战士","射手")
.groupBy("position")
.orderByDesc("count(id)");
//当前的sql语句查询的结果是一个订制数据,如查询总数只会得到一个数据,所以使用listMaps方法
List<Map<String, Object>> maps = heroService.listMaps(wrapper);
System.out.println(maps);
}
分页查询
MyBatisPlus中集成了分页功能,只需在项目的启动类中注入一个分页拦截器对象
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//创建一个MybatisPlusInterceptor对象
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//通过拦截器对象添加分页拦截器对象,设置数据库类型
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
无条件分页查询
@Test
void test1() {
//无条件分页查询
//1.创建一个分页模型对象,设置泛型,参数为当前页和每页显示的记录数
Page<Hero> pageInfo = new Page<>(1, 5);
//2.调用IService中的page方法,参数为分页模型对象, 返回值也是该对象
pageInfo = heroService.page(pageInfo);
//分页的相关信息都保存在分页模型对象pageInfo中
System.out.println("总页数" + pageInfo.getPages());
System.out.println("当前页" + pageInfo.getCurrent());
System.out.println("每页显示的记录数" + pageInfo.getSize());
System.out.println("总记录数" + pageInfo.getTotal());
System.out.println("分页后的集合" + pageInfo.getRecords());
System.out.println("是否有下一页" + pageInfo.hasNext());
System.out.println("是否有上一页" + pageInfo.hasPrevious());
}
条件分页查询
@Test
void test2(){
Page<Hero> pageInfo = new Page<>(1, 5);
QueryWrapper<Hero> wrapper = new QueryWrapper<>();
//分页查询所有法师
pageInfo = heroService.page(pageInfo,wrapper.eq("position","法师"));
pageInfo.getRecords().forEach(System.out::println);
}
代码生成器
MyBatisPlus中用于自动生成entity. mapper. service、 controller这些类和接口的工具
1.添加依赖:
<!--代码生成器依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<!--代码生成器模板引擎依赖-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
2.代码生成器类
修改数据库信息,输出目录为本地文件,设置父包名,以及设置需要生成的表名
package com.hqyj.question_sys.util;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
public class CodeGenerator {
public static void main(String[] args) {
//***数据库的url, 用户名和密码
FastAutoGenerator.create("jdbc:mysql://localhost:3306/question_sys?serverTimezone=Asia/Shanghai",
"root", "123456")
.globalConfig(builder -> {
builder.author("HQYJ") // 设置作者,生成文件的注释里的作者
.outputDir("D:\\idea培训文件\\Spring相关内容\\question_sys\\src\\main\\java"); //***指定输出目录
}).packageConfig(builder -> {
builder.parent("com.hqyj.question_sys"); //***设置父包名
// .controller("controller") //控制层包名
// .service("service") //service接口包名
// .serviceImpl("service.impl") //service接口实现包名
// .mapper("mapper") //mapper接口包名
// .xml("mapper.xml") //映射文件的包名(如果要生成的话,加上这句,去掉下面的.xml方法)
// .entity("entity"); //实体类的包名
}).strategyConfig(builder -> {
builder.addInclude("question_catalog","question") //***设置需要生成的表名
.controllerBuilder();//只生成Controller
//.enableRestStyle(); //生成的Controller类添加@RestController;
}).templateConfig(builder -> {
builder.xml(null); //禁止生成xml映射文件
}).execute();
}
}
Thymeleaf
SpringBoot官方建议使用的页面模板引擎,代替之前的JSP。
它是以HTML为基础,在HTML 中嵌入ava代码。
可以当作HTML页面打开查看页面内容
用法
1.添加依赖
<!-- thymeleaf页面模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.7.3</version>
</dependency>
2.在temlates目录下创建一个HTML页面
在页面的<html>标签中,加入属性
3.thymeleaf具体使用
双标签中的输出变量:th:text
<h1 th:text="${保存在作用域中的变量}">这里显示str变量</h1>
<h1 th:text="${str}">这里显示str变量</h1>
单标签中设置value值:th:value
<input type="text" th:value="${作用域中某个对象名}">
<input type="text" th:value="${变量.属性名}">
遍历:th:each
<table>
<tr th:each="变量:${作用域中的集合名}">
<td th:text="${变量.属性名}"></td>
</tr>
</table>
超链接获取全局路径:th:href="@{/}"
不带参数
<link th:href="@{/bootstrap-3.4.1-dist/css/bootstrap.css}" rel="stylesheet">
带参数
<a th:href="@{'/hero/delete?id='+${hero.id}}">删除</a>
表单提交路径:th:action="@{/}"
<form method="post" th:action="@{/hero/insert}">
</form>
判断:th:if
<h1 th:if="判断逻辑">用户信息</h1>
<h1 th:if="${userInfo.userName==null}">请登录</h1>
选中单选按钮和复选框:th:checked
<input type="radio" name="quesAns" value="a" th:checked="${question.quesAns=='a'}"> A
<input type="radio" name="quesAns" value="b" th:checked="${question.quesAns eq 'b'}"> B
<input type="radio" name="quesAns" value="c" th:checked="${question.quesAns=='c'}"> C
选中下拉菜单:th:selected
<div class="col-sm-10">
<select class="form-control" name="qcId">
<option th:selected="${qc.qcId eq question.qcId}" th:each="qc:${qcList}" th:value="${qc.qcId}"
th:text="${qc.qcName}"></option>
</select>
</div>
src属性:th:src
<script th:src="@{/bootstrap-3.4.1-dist/js/jquery-3.6.2.min.js}"></script>
内联表达式
Thymeleaf页面中的内联表达式
[[]]之间的表达式在Thymeleaf中称为内联表达式,可以用在script标签中拼接EL表达式
要在script标签中加入th:inline="javascript"属性
<script th:inline="javascript">
$(function () {
//遍历页码
$(".pno").each(function () {
//如果页码和当前页码一致,高度显示
if ($(this).text() == [[${pageInfo.current}]]) {
$(this).addClass("active");
}
});
});
</script>
Spring Data JPA
是一套Java访问数据库的规范,由一系列接口和抽象类构成。
SpringBoot集成Spring Data JPA
1.创建项目选择依赖
Lombok
Spring Web
MySQL Driver
Spring Data JPA
Spring Boot devTools
2.编辑配置文件,设置要链接的数据库信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/bookdb?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
# 设置数据库类型
spring.jpa.database=mysql
# 打印sql语句
spring.jpa.show-sql=true
3.创建实体类
类上添加@Entity注解
主键属性上
@Id注解标明主键
@GeneratedValue(strategy = GenerationTye.IDENTITY设置主键生成策略,数据库设置为自增
其他属性与字段名不一致或驼峰命名法
如果不相同,使用@Column(name="字段名")注解指定该属性对应的字段名
@Data
@Entity
public class BookInfo {
@Id//主键字段
//主键生成策略,GenerationType.IDENTITY表示MySQL自增
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer bookId;
private Integer typeId;
private String bookName;
private String bookAuthor;
//如果字段名和属性值不一致,使用@Column()指定字段名
@Column(name="book_info")
private Integer price;
private Integer bookNum;
private String publisherDate;
private String bookImg;
}
4.数据访问层接口
类上加@Repository注解
继承paRepository<实体类型,主键类型>接口
@Repository
public interface BookInfoDao extends JpaRepository<BookInfo,Integer> {
}
5.单元测试类
@SpringBootTest
class SpringbootjpaApplicationTests {
@Autowired
private BookInfoDao bookInfoDao;
@Test
void contextLoads() {
//查询所有
List<BookInfo> list = bookInfoDao.findAll();
list.forEach(System.out::println);
}
@Test
void insert(){
//添加
BookInfo bookInfo = new BookInfo();
bookInfo.setBookName("测试");
bookInfo.setTypeId(1);
bookInfo.setBookAuthor("测试");
bookInfo.setBookNum(200);
bookInfo.setPrice(40);
//save()方法调用时,如果对象中没有主键或主键值不存在,作为添加使用
BookInfo save = bookInfoDao.save(bookInfo);
//添加成功后,会自动获取主键自增的值
System.out.println(save);
}
@Test
void update(){
//修改
BookInfo bookInfo = new BookInfo();
bookInfo.setBookName("xxxxxx");
bookInfo.setTypeId(1);
bookInfo.setBookAuthor("xxxx");
bookInfo.setBookNum(200);
bookInfo.setPrice(40);
//save()方法调用时,如果对象中有主键且存在,作为修改使用
BookInfo save = bookInfoDao.save(bookInfo);
//修改成功后,返回修改后的对象
System.out.println(save);
}
//删除
@Test
void delete(){
//根据主键值删除,如果值不存在,会报错
bookInfoDao.deleteById(1);
//根据对象删除,如果对象中包含主键值则删除,如果没有值或不存在,不会报错
BookInfo bookInfo = new BookInfo();
bookInfo.setBookId(2);
bookInfoDao.delete(bookInfo);
}
@Test
void findOne(){
//根据主键查询,返回值Optional类型
Optional<BookInfo> byId = bookInfoDao.findById(3);
//isPresent()如果为true,表示查询到了数据
if (byId.isPresent()) {
//get()将查询到的数据转换为对应的实体类
BookInfo bookInfo = byId.get();
System.out.println(bookInfo);
}else {
System.out.println("未查询到数据");
}
}
}
常用方法
方法 | 返回值 | 作用 |
findAll() | List<T> | 查询所有数据 |
save(T entity) | T entity | 添加或修改,如果不存在主键属性或主键值不存在,执行添加;如果存在主键属性且有该主键值,执行修改 |
delete(T entity) | void | 根据对象删除。如果该对象中有主键属性且有该主键值,根据该主键值删除 |
findById(主键) | Optional<T> | 根据主键值查询。返回的对象调用isPresent()结果为true, 表示查询到了数据,继续调用get)得到查询到的对象。 |
JPA进阶
分页查询
在数据访问层中调用findAll(Pageable pageable)方法,即可实现分页
参数Pageable是org.springframework.data.domain包中的一个接口,通过其实现类
PageReguest,调用静态方法of(int page,int size),当做Pageable对象使用。
这里的page从0开始为第一页
//分页查询
@Test
void queryByPage(){
//PageRequest是Pageable的实现类调用PageRequest类中的静态方法of(int page,int size)
//这里的page的值0表示第一页
PageRequest of = PageRequest.of(0, 5);
//调用findAll(Pageable pageable)方法,返回分页模型对象
Page<BookInfo> pageInfo = bookInfoDao.findAll(of);
//分页相关数据
System.out.println("总记录数"+pageInfo.getTotalElements());
System.out.println("最大页数"+pageInfo.getTotalPages());
System.out.println("分页后的数据集合"+pageInfo.getContent());
System.out.println("当前页数"+pageInfo.getNumber());
System.out.println("每页显示的记录数"+pageInfo.getSize());
System.out.println("是否还有下一页"+pageInfo.hasNext());
System.out.println("是否还有上一页"+pageInfo.hasPrevious());
}
条件查询
在JPA中使用自定义方法名自动生成对应的sql语句,实现条件查询
如在dao中定义了queryAllById(int id)方法,就表示根据id查询,自动生成sql语句
方法命名格式
[xxx] [By] [字段对应的属性名] [规则] [Or/And] [字段对应的属性名] [规则] ..
xxx可以是find, get. query. search
方法如果有参数,参数的顺序和方法名中的参数顺序一致
//findByBookNameAndBookAuthor(String bookName,String bookAuthor)
//对应的sql语句
select * from book where book _name =? and book author=?
常用规则
规则 | 方法名 | SQL |
指定值 | findByBookName(String name) | book_name=? |
Or/And | findByNameOrBookAuthor(String name,Strng author)findByNameAndBookAuthor(String name,Strng author) | book_name=? or book_author=?book_name=? and book_author=? |
After/Before | findByBookPriceAfter(double number)findByBookPriceBefore(double number) | book_price>numberbook_price<number |
GreatThan/LessThan | queryAllByBookNumGreaterThan(int num)queryAllByBookNumLessThan(int num) | book_num>numbook_num<num |
GreatThanEqual/LessThanEqual | queryAllByBookNumGreaterThanEqual(int num)queryAllByBookNumLessThanEqual(int num) | book_num>=numbook_num<=num |
Between | findByBookNumBetween(int min,int max) | book_num between min and max |
IsNull/IsNotNull | findAllByPublisherDateIsNull()findAllByPublisherDateIsNotNull | publisher_date is nullpublisher_data is not null |
Like/NotLike | getAllByBookNameLike(String keyword)getAllByBookNameNotLike(String keyword) | book_name like keywordbook_name not like keyword |
Contains/NotContains | findByBookNameContains(String keyword)findByBookNameNotContains(String keyword) | book_name like '%keyword%'book_name not like '%keyword%' |
StartsWith/EndsWith | findByBookNameStartsWith(String firstName)findByBookNameEndsWith(String endName) | book_name like 'firstName%'book_name like '%endName' |
无条件排序:findAllByOrderBy字 段[Desc/Asc] | findAllByOrderByBookId()findAllByOrderByBookIdDesc() | order by book_id ascorder by book_id desc |
有条件排序:findAllBy条件 OrderBy字段[Desc/Asc] | findAllByTypeIdOrderByBookIdDesc()findAllByTypeIdOrderByBookId() | type_id = ? order by book_id desctype_id = ? order by book_id |
@Repository
public interface BookInfoDao extends JpaRepository<BookInfo, Integer> {
//指定值查询
//根据书名查询
List<BookInfo> getAllByBookName(String x);
//查询价格大于指定值 字段对应的属性名 After/GreaterThan
List<BookInfo> findAllByPriceAfter(int price);
//查询价格小于于指定值 字段对应的属性名 Before/LessThan
List<BookInfo> findAllByPriceLessThan(int price);
//查询库存大于等于指定值 GreaterThanEqual
List<BookInfo> queryAllByBookNumGreaterThanEqual(int num);
//查询库存在指定闭区间内 Between(int min,int max)
List<BookInfo> findAllByBookNumBetween(int min, int max);
//空值查询 null
//查询出版日期为空 IsNull/IsNotNull
List<BookInfo> findAllByPublisherDateIsNull();
//书名中带有关键字 Like/NotLike 实参一定要使用%或_
List<BookInfo> getAllByBookNameLike(String keyword);
//作者名中带有关键字 Contains/NotContains 实参只需要关键字
List<BookInfo> getAllByBookAuthorContains(String keyword);
//指定作者的姓 指定开头/结尾 StartsWith/EndsWith
List<BookInfo> getAllByBookAuthorStartsWith(String keyword);
//查询所有数据,按价格降序 无条件排序 OrderBy字段[Desc/Asc]
List<BookInfo> getAllByOrderByPriceDesc();
//查询指定类型,按id降序
List<BookInfo> getAllByTypeIdOrderByBookIdDesc(Integer typeId);
//根据作者和书名的关键字分页查询
Page<BookInfo> getAllByBookNameContainsOrBookAuthorContains(
String nameKeyword,
String authorKeyword,
Pageable pageable);
}
条件分页查询
只需在定义方法时,将方法的返回值设置为Page类型,在参数中就如Pageable对象
//根据作者和书名的关键字分页查询
Page<BookInfo> getAllByBookNameContainsOrBookAuthorContains(
String nameKeyword,
String authorKeyword,
Pageable pageable);
//根据作者和书名关键字分页查询
@Test
void test3(){
Page<BookInfo> pageInfo = dao.getAllByBookNameContainsOrBookAuthorContains("龙","山",PageRequest.of(0,5));
pageInfo.getContent().forEach(System.out::println);
}
聚合函数分组查询
自定义SQL
在数据访问层接口中的方法上,可以加入@Query注解,默认要使用HQL(Hibernate专用)格式的语句。
如果要使用原生的SQL语句,需要添加nativeQuery=true属性, 用value属性定义SQL语句
//测试查询
/*在JPA中,如果要使用自定义的SQL语句
* nativeQuery = true 开启原生SQL语句
* value="sqL语句"
* */
//自定义sql:根据作者分类,查询作者的图书总量
@Query(nativeQuery=true,value="select book_author,count(book_id) from book_info group by book_author")
List testQuery();
//根据类型分组,查询魅族图书的平均价格
@Query(nativeQuery=true,value="select type_id,avg(book_price) from book_info group by type_id")
List testQuery2();
//自定义sql:根据作者分类,查询作者的图书总量
@Test
void test5(){
List list = dao.testQuery();
//查询的结果为集合,集合中保存的是每一行数据
for (Object row : list) {
//每一行页数一个对象数组
Object[] obj = (Object[]) row;
//根据索引得到查询出的内容
System.out.println(obj[0]+"-----"+obj[1]);
}
}
@Test
void test6(){
List list = dao.testQuery2();
for (Object object : list) {
Object[] obj = (Object[]) object;
System.out.println(obj[0]+"-----"+obj[1]);
}
}
自定义SQL中带参数
SQL语句中的"XXX"表示参数
如果方法的形参名和xxx一致时直接使用,如果不一致,在形参上加入@Param注解设置形参名
//根据作者查询其图书总库存:select book_author,sum(book_num) from book_info where book_author='金庸'
/*使用":形参名"在SQL语句中带参数
在方法中通过@Prama定义形参
* */
@Query(nativeQuery=true,value="select book_author,sum(book_num) from book_info where book_author=:zuozhe")
List testQuery3(@Param("zuozhe") String xxx);
@Test
void test7(){
List list = dao.testQuery3("金庸");
for (Object object : list) {
Object[] obj = (Object[]) object;
System.out.println(obj[0]+"-----"+obj[1]);
}
}
关联查询
主表:book_type
从表:book_info
//如果根据外键字段查询,方法名写为By外键实体类属性名_属性名
List<BookInfo> getAllByBt_TypeId(Integer typeId);
//根据图书类型查找书籍
List<BookInfo> bookInfos = dao.getAllByBt_TypeId(2);
bookInfos.forEach(System.out::println);