华清远见-重庆中心-框架个人总结
框架
一套規范。
實際是他人實現的一系列接口和類的集合。通入導入對應框架的jar文件(maven項目導入對應的依賴),進行適當的配置,就能使用其中的所有內容。
開發者可以省去很多模板代碼,如dao中的CRUD,MVC模式下層與層之間的關聯。只需要集中精力實現項目中的業務邏輯部分
主流框架
Spring、SpringMVC、MyBatis、MyBatisPlus、Hibernate、JPA等。
SSH:最初是Spring+Stucts2+Hibernate組成,之后Stucts2被SpringMVC取代。
SSM:Spring+SpringMVC+MyBatis
新項目使用SpringBoot,早起的SSH項目由于維護成本高,基本不會推翻重做,但會維護一些SSM項目。
無論是SSH還是SSM,Spring、SpringMVC必不可少。從2004年推出至今,依舊是主流框架中不可獲取的一部分。
SPring
一個輕量級開源的Java框架。是一個管理項目中對象的容器,同時也是其他框架的粘合器,目的就是對項目進行解耦。
Spring的核心是IOC控制反轉和AOP面向切面編程
Spring 應用
1.創建一個普通的Maven項目,不選擇模板
2.添加Spring核心依賴
jdk8用5.x版本
<!-- spring-context表示spring核心容器 -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.23</version>
</dependency>
3.創建一個Java類
package com.hqyj.spring01;
/*
* 定義一個普通的Java類
* PlainOrdinaryJavaObject pojo 相當于簡化的javabean
* entity vo dto pojo 都是在描述實體類
* */
public class PlainOrdinaryJavaObject {public void fun(){System.out.println("一個PlainOrdinaryJavaObject(普通的Java類)對象");}
}
4.創建Spring配置文件
在resources目錄下,創建一個xml文件,選擇spring config,通常命名為application
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--bean標簽表示,在Spring容器中,注入某個類的對象--><!--class屬性表示要注入哪個類,寫類的全限定名(包名+類名)--><!--id表示給類的對象的名稱--><bean class="com.hqyj.spring01.PlainOrdinaryJavaObject" id="pojo"></bean></beans>
5.創建main方法所在類
解析Spring配置文件,獲取注入的對象。
package com.hqyj.spring01;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {//創建一個自定義類的對象//傳統方式創建對象//PlainOrdinaryJavaObject pojo = new PlainOrdinaryJavaObject();//使用spring容器獲取對象//創建一個用于解析Spring配置文件的對象,參數為配置文件的名稱。實際就是獲取Spring容器ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");//獲取容器中的對象/*只通過名稱獲取,返回值為Object類型,需要轉換Object obj = context.getBean("pojo");PlainOrdinaryJavaObject pojo=(PlainOrdinaryJavaObject) obj;*///使用這種方式,直接用指定類型接收PlainOrdinaryJavaObject pojo = context.getBean("pojo", PlainOrdinaryJavaObject.class);//能成功調用方法,說明Spring管理了該類的對象pojo.fun();//關閉Spring容器context.close();}
}
bean標簽常用屬性
| 屬性 | 作用 |
|---|---|
| class | 定義類的全限定名 |
| id | 定義對象的名稱 |
| lazy-init | 是否為懶加載。默認值為false,在解析配置文件時就會創建對象。設置為true表示懶加載,只有在getBean()時才會創建對象。 |
| scope | 單例/原型模式。默認值為singleton,表示單例模式,只會創建一個對象。設置為prototype,表示原型模式,每調getBean()就創建一個對象。 |
| init-method | 初始化時觸發的方法。在創建完該對象時自動調用的方法。該方法只能是無參方法,該屬性的值只需要寫方法名即可 |
| destory-method | 銷毀時觸發的方法。Spring容器關閉時自動調用的方法,該方法只能是無參方法。只有在單例模式下有效。 |
屬性注入
給某個bean添加屬性的方式有兩種:構造器注入和setter注入
setter注入
這種方式注入屬性時,類中必須要有set方法
在bean標簽中,加入<property></property>標簽,
該標簽的name屬性通常表示該對象的某個屬性名,但實際是setXXX()方法中的XXX單詞。
如有age屬性,但get方法為getNianLing(),name屬性就需要寫成nianLing。
該標簽的value屬性表示給該類中的某個屬性賦值,該屬性的類型為原始類型或String。
該標簽的ref屬性表示給該類中除String以外的引用類型屬性賦值,值為Spring容器中另一個bean的id。
<!--注入Car類對象并用set方式注入其屬性-->
<bean class="com.hqyj.spring01.Car" id="c"><!--該屬性是字符串或原始類型,使用value賦值--><property name="brand" value="寶馬"></property><!--name并不是類中是屬性名,而是該屬性對應的getXXX()方法中XXX的名稱--><!--如Car類中有color屬性,但get方法名為getColo(),這里就要寫為colo--><property name="colo" value="白色"></property>
</bean><!--注入Person類對象并用set方式注入其屬性-->
<bean class="com.hqyj.spring01.Person" id="p1"><property name="name" value="王海"></property><property name="age" value="22"></property><!--屬性是引用類型,需要通過ref賦值,值為另外的bean的id ref即references--><property name="car" ref="c"></property>
</bean>
構造方法注入
這種方式注入屬性時,類中必須要有相應的構造方法
在bean標簽中,加入<constructor-arg></constructor-arg>標簽,
該標簽的name屬性表示構造方法的參數名,index屬性表示構造方法的參數索引。
賦值時,原始類型和字符串用value,引用類型用ref。
<!--注入Person類對象并用構造方法注入其屬性-->
<bean class="com.hqyj.spring01.Person" id="p2"><!--constructor-arg表示構造方法參數 name是參數名 index是參數索引--><constructor-arg name="name" value="張明"></constructor-arg><constructor-arg index="1" value="20"></constructor-arg><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;//放映時間,最終格式為yyyy/MM/dd HH:mm:ssprivate String showTime;
}
List類型的屬性
<!--注入Movie對象-->
<bean class="com.hqyj.spring01.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="2010/06/01 0:0:0"></property>
</bean>
Set類型的屬性
<!--注入PetShop對象-->
<bean class="com.hqyj.spring01.test3.PetShop" id="petShop"><property name="shopName" value="xxx寵物店"></property><property name="petSet"><!--Set類型的屬性--><set><!--如果中保存的是原始類型或字符串,使用value子標簽--><!--如果保存的是引用類型,使用ref子標簽加bean屬性設置對應的id--><ref bean="p1"></ref><ref bean="p2"></ref><ref bean="p3"></ref></set></property>
</bean>
Map類型的屬性
<!--注入Cinema對象-->
<bean class="com.hqyj.spring01.Cinema" id="cinema"><property name="name" value="萬達影城"></property><property name="timeTable"><!--Map類型屬性賦值,標明鍵和值的類型--><map key-type="java.lang.Integer" value-type="com.hqyj.spring01.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>
屬性值如果通過某個方法調用而來
如使用String保存yyyy/MM/dd格式的日期,需要通過SimpleDateFormat對象調用parse()方法而來
<!--SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");-->
<!--注入SimpleDateFormat對象,設置日期格式-->
<bean class="java.text.SimpleDateFormat" id="sdf"><!--如果構造方法注入時,該構造方法只有一個參數,可以不用寫name或index--><constructor-arg value="yyyy/MM/dd"></constructor-arg>
</bean>
<!--Date now = new Date();-->
<!--注入Date對象-->
<bean class="java.util.Date" id="now"></bean><!--注入Pet對象-->
<bean class="com.hqyj.spring01.test3.Pet" id="p1"><property name="petType" value="哈士奇"></property><property name="petNickName" value="小哈"></property><!--使用當前時間作為該屬性的值,以yyyy/MM/dd--><!--sdf.format(now);--><property name="petBirthday"><!--如果某個值是通過某個bean調用了某個方法而來--><bean factory-bean="sdf" factory-method="format"><!--方法的實際參數--><constructor-arg ref="now"></constructor-arg></bean></property>
</bean>
屬性自動注入autowire
以上所有案例中,如果要在A對象中注入一個引用類型的對象B,都是手動將對象B注入到對象A中。
如在Person中注入Car對象,在Cinema中注入Movie等。
這種情況下,如果當某個被注入的bean的id更改后,所有引用了該bean的地方都要進行修改。
所以將手動注入更改為自動注入(自動裝配),就無需添加相應的<property>標簽,甚至可以無需定義bean的id。
實現自動注入
在某個bean標簽中,加入autowire屬性,設置值為"byName"或"byType"。通常設置為"byType"。
Car類
package com.hqyj.spring01.test1;import java.util.HashMap;public class Car {private String brand;private String color;@Overridepublic String toString() {return "Car{" +"brand='" + brand + '\'' +", color='" + color + '\'' +'}';}public String getBrand() {return brand;}public void setBrand(String brand) {this.brand = brand;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}
}
Person類
package com.hqyj.spring01.test1;public class Person {private String name;private int age;private Car car;@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", car=" + car +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Car getCar() {return car;}//自動注入時,必須要有該方法//byType方式自動注入,會自動在容器中尋找該方法參數類型//byName方式自動注入,會自動在容器中尋找該方法setCar中的car這個idpublic void setCar(Car car) {this.car = car;}
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--在容器中注入Car類型的bean--><bean class="com.hqyj.spring01.test1.Car" id="car"> <property name="brand" value="寶馬"></property><property name="colo" value="白色"></property></bean><!--注入Person類的bean--><!--autowire="byType"表示自動檢測該類中是否需要使用引用類型參數,如果容器中正好有唯一的一個對應類型的bean,就會自動賦值給對應的屬性--><bean class="com.hqyj.spring01.test1.Person" id="person" autowire="byType"><property name="name" value="趙明"></property><property name="age" value="26"></property></bean><!--autowire="byName"表示自動檢測該類中的setXXX方法,如果某個setXXX方法的XXX和容器某個對象的id對應,就會自動賦值給對應的屬性--><bean class="com.hqyj.spring01.test1.Person" id="person2" autowire="byName"><property name="name" value="王海"></property><property name="age" value="26"></property></bean>
</beans>
Main
package com.hqyj.spring01.test1;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");Person person = context.getBean("person", Person.class);//此時打印時,會輸出姓名、年齡和Car對象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的對象自動裝配
在Web項目中,可以利用自動裝配,在控制層中自動裝配業務邏輯層的對象,在業務邏輯層中自動裝配數據訪問層的對象。
屬性自動注入練習
在電影院案例的基礎上
1.創建一個dao層CinemaDao類,包含Cinema對象,定義一個fun()方法,輸出Cinema對象
package com.hqyj.spring01.test2.dao;import com.hqyj.spring01.test2.Cinema;/*
* 定義數據訪問層
* 模擬查詢數據庫得到結果
* */
public class CinemaDao {private Cinema cinema;public void setCinema(Cinema cinema) {this.cinema = cinema;}public void fun(){System.out.println(cinema);}
}
2.創建一個controller層CinemaController類,包含CinemaDao對象,定義方法fun()調用CinemaDao中的fun()方法
package com.hqyj.spring01.test2.controller;import com.hqyj.spring01.test2.dao.CinemaDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*
* 定義控制層類,該層需要使用下一層dao中的方法
* */
public class CinemaController {//這里無需創建對象,讓Spring自動注入CinemaDao對象private CinemaDao cinemaDao;public void setCinemaDao(CinemaDao dao) {this.cinemaDao = dao;}public void fun(){cinemaDao.fun();}
}
最終在Main方法中獲取Controller對象后,調用fun()
public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("movie.xml");CinemaController controller = context.getBean("controller", CinemaController.class);controller.fun();
}
配置文件
<!--省略movie對象--><!--注入Cinema對象-->
<bean class="com.hqyj.spring01.test2.Cinema" id="cinema"><property name="name" value="萬達影城"></property><property name="timeTable"><!--Map類型屬性賦值,標明鍵和值的類型--><map key-type="java.lang.Integer" value-type="com.hqyj.spring01.test2.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><!--該類中有一個Cinema類型的屬性,這里使用了autowire=byType自動裝配,會在容器中尋找Cinema類型的對象自動賦值給屬性-->
<bean class="com.hqyj.spring01.test2.dao.CinemaDao" autowire="byType"></bean><!--該類中有一個CinemaDao類型的屬性,這里使用了autowire=byType自動裝配,會在容器中尋找CinemaDao類型的對象自動賦值給屬性-->
<bean class="com.hqyj.spring01.test2.controller.CinemaController" id="controller" autowire="byType"></bean>
核心注解
在Spring配置文件中加入
<!--設置要掃描的包,掃描這個包下所有使用了注解的類-->
<context:component-scan base-package="com.hqyj.spring02.bookSystem"></context:component-scan>
類上加的注解
- @Component
- 當一個類不好歸納時,定義為普通組件
- @Controller
- 定義一個類為控制層組件
- @Service
- 定義一個類為業務層組件
- @Repository
- 定義一個類為持久層(數組訪問層)組件
- @Lazy/@Lazy(value=true)
- 設置該類為懶加載。
- @Scope(value=“singleton/prototype”)
- 設置為單例/原型模式。
屬性上加的注解
-
@Autowired
-
優先使用byType方式從Spring容器中獲取對應類型的對象自動裝配。先檢索Spring容器中對應類型對象的數量,如果數量為0直接報錯;數量為1直接裝配
數量大于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方式,要求對象有且只有一個,否則也會報錯。
-
在Web項目中使用Spring
1.創建基于Maven的web-app項目
2.添加依賴
<!--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集成spring-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.3.23</version>
</dependency>
3.在main目錄下創建java和resources目錄,修改web.xml版本為4.0
4.在resources目錄下創建Spring配置文件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.springweb"></context:component-scan>
</beans>
5.創建一個類,使用@Componet注解將其注入到Spring容器中
package com.hqyj.springweb.entity;import org.springframework.stereotype.Component;@Component
public class Pojo {public void fun(){System.out.println("hello springweb!");}
}
6.在web.xml中配置監聽器用于初始化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"><!--配置監聽器ContextLoaderListener--><listener><!--監聽器全限定名--><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!--定義全局參數contextConfigLocation用于讀取Spring配置文件--><context-param><!--參數名固定contextConfigLocation--><param-name>contextConfigLocation</param-name><!--只是Spring配置文件的路徑 classpath:表示從根目錄出發--><param-value>classpath:application.xml</param-value></context-param>
</web-app>
7.創建一個Servlet,訪問該Servlet,獲取Spring容器,從容器中獲取注入的對象
package com.hqyj.springweb.controller;import com.hqyj.springweb.entity.Pojo;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@WebServlet("/hello")
public class MyServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//獲取Spring容器WebApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(getServletContext());//從容器中獲取某個beanPojo pojo = app.getBean("pojo", Pojo.class);pojo.fun();}
}
在Spring-web項目中使用Spring-jdbc
Spring提供了一套自己的JDBC解決方案,相較于自己寫的JDBC,省去了繁瑣的過程(獲取連接、關閉),簡化了對數據庫的操作。
Spring-jdbc后續會用其他持久層框架代替,如MyBatis、JPA。
1.引入相關依賴
當前案例中核心的依賴是 spring-jdbc
<!--servlet-->
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</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容器-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.23</version>
</dependency>
<!--web集成spring-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.3.23</version>
</dependency>
<!-- spring-jdbc -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.23</version>
</dependency>
2.創建包結構(controller、service、dao、entity),創建實體類
public class Hero {private int id;private String name;private String position;private String sex;private int price;private String shelfDate;//省略get/set、構造方法、toString等
}
3.在Spring配置文件中配置數據源和JDBC模板
<?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.springweb"></context:component-scan><!--配置數據源--><bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource"><!--配置MySQL驅動--><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property><!--配置MySQL地址--><property name="url" value="jdbc:mysql://localhost:3306/gamedb?serverTimezone=Asia/Shanghai"></property><property name="username" value="root"></property><property name="password" value="root"></property></bean><!--配置JDBC模板--><bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate"><!--設置要操作的數據源--><property name="dataSource" ref="dataSource"></property></bean>
</beans>
4.數據訪問層(dao)的寫法
package com.hqyj.springweb.dao;import com.hqyj.springweb.entity.Hero;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;import java.util.List;@Repository
public class HeroDao {@Autowired//通過jdbc模板對象實現CRUDprivate JdbcTemplate jdbcTemplate;/** 查詢所有* */public List<Hero> queryAll() {//定義sql語句String sql = "select * from hero";//BeanPropertyRowMapper對象表示將查詢到的字段與指定的類中的屬性進行映射BeanPropertyRowMapper<Hero> mapper = new BeanPropertyRowMapper<>(Hero.class);//query()方法執行查詢List<Hero> list = jdbcTemplate.query(sql, mapper);return list;}/** 刪除* */public boolean delete(int id) {String sql = "delete from hero where id=?";int i = jdbcTemplate.update(sql, id);return i > 0;}/** 修改* */public boolean update(Hero hero) {String sql = "update hero set name=?,sex=?,position=?,price=?,shelf_date=? where id=?";int i = jdbcTemplate.update(sql, hero.getName(), hero.getSex(), hero.getPosition(), hero.getPrice(), hero.getShelfDate(), hero.getId());return i > 0;}/** 添加* */public boolean insert(Hero hero) {String sql = "insert into hero values(null,?,?,?,?,?)";int i = jdbcTemplate.update(sql, hero.getName(), hero.getSex(), hero.getPosition(), hero.getPrice(), hero.getShelfDate());return i > 0;}/** 根據id查詢* */public Hero findById(int id) {String sql = "select * from hero where id=?";BeanPropertyRowMapper<Hero> mapper = new BeanPropertyRowMapper<>(Hero.class);Hero hero = jdbcTemplate.queryForObject(sql, mapper, id);return hero;}
}
忽略后續service、servlet、jsp等;
JDBCTemplate常用方法
| 方法 | 作用 | 說明 |
|---|---|---|
| query(String sql,RowMapper mapper) | 無條件查詢 | 返回值為List集合 |
| update(String sql) | 無條件更新(刪除、修改) | 返回值為受影響的行數 |
| query(String sql,RowMapper mapper,Object… objs) | 條件查詢 | 可變參數為?的值 |
| update(String sql,Object… objs) | 條件更新(增加、刪除、修改) | 可變參數為?的值 |
| queryForObject(String sql,RowMapper mapper) | 無條件查詢單個對象 | 返回值為指定對象 |
| queryForObject(String sql,RowMapper mapper,Object… objs) | 條件查詢單個對象 | 返回值為指定對象 |
| execute(String sql) | 執行指定的sql | 無返回值 |
SpringMVC
在Web階段中,控制層是由Servlet實現,傳統的Servlet,需要創建、重寫方法、配置映射。使用時極不方便,SpringMVC可以替換Servlet。
SpringMVC是Spring框架中位于Web開發中的一個模塊,是Spring基于MVC設計模式設計的輕量級Web框架。
SpringMVC提供了一個DispatcherServlet的類,是一個Servlet。它在指定映射(通常設置為/或*.do)接收某個請求后,調用相應的模型處理得到結果,再通過視圖解析器,跳轉到指定頁面,將結果進行渲染。
原理大致為:配置SpringMVC中的DispatcherServlet,將其映射設置為/或.do。*
如果是/表示一切請求先經過它,如果是*.do表示以.do結尾的請求先經過它,
它對該請求進行解析,指定某個Controller中的某個方法,這些方法通常返回一個字符串,
這個字符串是一個頁面的名稱,再通過視圖解析器,將該字符串解析為某個視圖的名稱,跳轉到該視圖頁面。
使用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
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--設置掃描使用了注解的根包--><context:component-scan base-package="com.hqyj.springmvc"></context:component-scan> </beans> -
在web.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><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.xml
- 配置要掃描的控制層類所在的包名
- 配置內部資源視圖解析器以及視圖路徑的前后綴
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--掃描控制層所在的包--><context:component-scan base-package="com.hqyj.springmvc.controller"></context:component-scan><!--配置內部資源視圖解析器--><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><!--最終控制層跳轉的頁面所在的路徑及頁面自身后綴名--><!--jsp頁面不建議直接通過瀏覽器訪問。在WEB-INF目錄下在資源,無法通過瀏覽器直接方法,所以將jsp保存在WEB-INF目錄下,最好創建一個pages--><property name="prefix" value="/WEB-INF/pages/"></property><!--現階段使用jsp輸出數據,所以后綴為.jsp--><property name="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的初始化參數,用于讀取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><!--設置該servlet的映射為/或*.do--><url-pattern>/</url-pattern> </servlet-mapping>
5.在WEB-INF目錄下創建一個pages目錄,在其中創建一個welcome.jsp頁面
- 通常jsp頁面不允許被瀏覽器直接訪問,需要保存在WEB-INF目錄下
6.編寫控制層代碼
-
在controller包下創建一個類,加上@Controller注解
-
該類中定義的方法方法加入@RequestMapping()注解表示訪問該方法的映射
-
該類中定義的方法返回值通常為字符串,表示某個頁面的名稱,也可以是另一個controller中的方法映射名
package com.hqyj.springmvc.controller;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;//加入@Controller注解表示該類是一個控制層類,替換之前的servlet @Controller //該注解如果只設置請求映射,直接填字符串 @RequestMapping("/first") public class HelloController {//該注解如果還有其他參數要設置,路徑用path賦值@RequestMapping(path="/hello")public String hello(){//返回的字符串是某個頁面的名稱或另一個控制層中方法的請求映射return "welcome";} }
將項目部署到tomcat后,訪問http://localhost:8080/SpringMVC_war_exploded/first/hello,即可跳轉到指定頁面
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}") public String fun(@PathVariable("path") String pageName){return pageName; } //當前方法如果通過"localhost:8080/項目名/hello"訪問,就會跳轉到hello.jsp //當前方法如果通過"localhost:8080/項目名/error"訪問,就會跳轉到error.jsp //映射中的/{path}就是獲取路徑中的hello或error,將其賦值給形參 //通常用于跳轉指定頁面
-
-
@RequestParam(value=“傳遞的參數名”,defaultValue =“沒有傳遞參數時的默認值”)
- 該注解寫在某個方法的某個參數上
- 用于獲取提交的數據,可以設置默認值在沒有提交數據時使用
控制層中獲取請求時傳遞的參數
-
controller中方法的形參名和表單的name或?后的參數名一致
表單或a標簽
<form action="${pageContext.request.contextPath}/first/sub"><input type="text" name="username"><input type="text" name="password"><input type="submit" > </form><a href="${pageContext.request.contextPath}/first/sub?username=admin">xxx</a>controller
public String login(String username,int password){//此時可以正常獲取//無關數據類型,但是提交數據時必須是對應的類型,否則會有400錯誤 } -
controller中方法的形參名和表單的name或?后的參數名不一致
controller
public String login(@RequestParam(value="username",defaultValue="admin")String name,@RequestParam("username")int pwd){//如果沒有提交數據時,會使用設置的默認值 } -
如果傳遞的參數都是某個實體類中的屬性時
User類
package com.hqyj.springmvc.entity;public class User {private String username;private String password;@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", password='" + password + '\'' +'}';}public User() {}public User(String username, String password) {this.username = username;this.password = password;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;} }controller
@RequestMapping("/login") public String login(User user){//某個實體類的屬性和提交的參數名一致時,可以直接將對象作為形參,會自動將對應參數賦值給對應屬性System.out.println(user);return ""; }
SpringMVC中的跳轉
-
控制層跳轉到某個jsp頁面
-
在控制層中定義方法,這種方式跳轉,屬于請求轉發
-
如果要使用重定向跳轉,在頁面名之前添加"redirect:"
@RequestMapping("/hello") public String hello(){//返回頁面名稱return "hello";//請求轉發 }@RequestMapping("/hello") public ModelAndView hello(ModelAndView mav){//設置頁面名稱mav.setViewName("hello");return mav; } -
在springmvc配置文件中
<mvc:view-controller path="請求映射" view-name="頁面名稱"></mvc:view-controller> <!-- 訪問項目根目錄,跳轉到welcome.jsp頁面 --> <mvc:view-controller path="/" view-name="welcome"></mvc:view-controller> <!-- 這個標簽使用時,會讓@RequesMapping失效,如果要共存,添加以下標簽 --> <!--來自于xmlns:mvc="http://www.springframework.org/schema/mvc" --> <mvc:annotation-driven></mvc:annotation-driven>
-
-
控制層跳轉到另一個控制層中的方法
-
方法的返回值為"redirect/forward:另一個控制層中方法的映射名"
@RequestMapping("/hello") public String hello(){return "redirect:hero";//使用重定向的方式,跳轉到映射名為hero的控制層return "forward:hero"//使用請求轉發的方式,跳轉到映射名為hero的控制層 }
-
-
jsp頁面跳轉另一個jsp頁面
-
當前項目中jsp頁面都在WEB-INF下,無法直接訪問,a標簽同樣如此,只能通過控制層跳轉頁面
-
定義用于跳轉頁面控制層類
package com.hqyj.springmvc.controller;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping;/** 定義一個用于跳轉指定頁面或controller的控制層* */ @Controller public class ToPageController {/*項目啟動時或直接訪問根目錄,跳轉到指定的controller*/@RequestMapping("/")public String toIndex() {return "redirect:/hero/queryAll";}/** 這個方法的作用:會將請求中第一個/后的單詞截取出來命名為path賦值給參數page* 如 localhost:8080/web/hero,就會識別出hero,return "hero";* 就會跳轉到 /WEB-INF/pages/hero.jsp頁面* */@RequestMapping("/{path}")public String toPage(@PathVariable("path") String page) {return page;} } -
這時在頁面中這樣跳轉
<%--這個路徑實際是/項目名/addHero,會截取addHero,跳轉到/項目名/WEB-INF/pages/addHero.jsp--%> <a href="${pageContext.request.contextPath}/addHero">添加</a>
-
ORM
Object Relational Mapping
對象關系映射 創建Java類的對象,將其屬性和數據庫表中的字段名之間構建映射關系。 這樣通過操作該類創建的一個對象,就能操作數據庫中對應的數據表。 ORM框架就是對象關系映射框架,用于簡化JDBC。 主流的ORM框架有Hibernate、JPA、MyBatis、MyBatisPlus等 ## 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 mysql druid spring-jdbc jstl
4.配置Spring 創建application.xml 掃描項目根包 配置web.xml 設置全局參數: ,讀取application.xml文件 設置全局監聽器:ContextLoaderListener,初始化Spring容器 5.配置SpringMVC 創建springmvc.xml 掃描控制層所在包 設置內部資源視圖解析器:InternalResourceViewResolver,設置前后綴 配置web.xml 設置請求分發器:DispatcherServlet,在其中讀取springmvc.xml配置文件 過濾器解決請求中文亂碼:CharacterEncodingFilter
6.配置MyBatis 創建mybatis-config.xml(官網模板) 在application.xml中注入 數據庫連接池Druid:DruidDataSource SQL會話工廠:SqlSessionFactoryBean 映射掃描配置器:MapperScannerConfigurer
7.執行sql語句 創建dao層接口,定義方法 在resoureces下創建mapper目錄創建sql映射文件xx.xml(官網模板),在其中定義sql語句
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 configurationPUBLIC "-//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") int bookNum,@Param("updateId") int bookId);
}
service層
package com.hqyj.ssm02.service;import com.hqyj.ssm02.dao.BookInfoDao;
import com.hqyj.ssm02.entity.BookInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class BookInfoService {@Autowiredprivate BookInfoDao bookInfoDao;public List<BookInfo> queryAll() {return bookInfoDao.queryAll();}public boolean delete(int id) {return bookInfoDao.delete(id) > 0;}public void insert(BookInfo bookInfo) {bookInfoDao.insert(bookInfo);}public BookInfo findById(int no) {return bookInfoDao.findById(no);}public void update(BookInfo bookInfo) {bookInfoDao.update(bookInfo);}
}
controller層
package com.hqyj.ssm02.controller;import com.hqyj.ssm02.entity.BookInfo;
import com.hqyj.ssm02.entity.BookType;
import com.hqyj.ssm02.service.BookInfoService;
import com.hqyj.ssm02.service.BookTypeService;
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 org.springframework.web.bind.annotation.RequestParam;import java.util.List;@Controller
@RequestMapping("/bookInfo")
public class BookInfoController {@Autowiredprivate BookInfoService bookInfoService;//注入BookTypeService,用于獲取所有圖書類型@Autowiredprivate BookTypeService bookTypeService;@RequestMapping("/queryAll")public String queryAll(Model model) {List<BookInfo> list = bookInfoService.queryAll();model.addAttribute("list", list);return "bookInfoList";}@RequestMapping("/delete")public String delete(int id) {//如果在頁面中傳遞的參數名和方法的形參名一致,會自動獲取數據賦值if (bookInfoService.delete(id)) {//增刪改執行后,使用重定向跳轉return "redirect:queryAll";}return "error";}@RequestMapping("/insert")//如果表單提交的參數和方法的形參名一致,自動獲取并賦值//如果表單提交的所有參數正好是一個實體類對象,可以用對應的實體類對象獲取public String insert(BookInfo bookInfo){bookInfoService.insert(bookInfo);return "redirect:queryAll";}@RequestMapping("/findById")//如果表單提交的參數名和方法的參數名不一致,使用@RequestParam("提交的參數名")public String findById(@RequestParam("id") int no,Model model){//查詢對應的圖書信息BookInfo byId = bookInfoService.findById(no);model.addAttribute("book",byId);//查詢所有的圖書類型,保存到請求中List<BookType> bookTypeList = bookTypeService.queryAll();model.addAttribute("btList",bookTypeList);return "bookEdit";}@RequestMapping("/update")public String update(BookInfo bookInfo){bookInfoService.update(bookInfo);return "redirect:queryAll";}}
sql映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//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_infowhere book_id = #{no}</select><!--根據id刪除--><delete id="delete">deletefrom book_infowhere book_id = #{id}</delete><!--添加--><insert id="insert">insert into book_infovalues (null, #{typeId}, #{bookName}, #{bookAuthor}, #{bookPrice}, #{bookNum}, #{publisherDate}, #{bookImg})</insert><!--修改--><update id="update">update book_infoset book_price=#{newPrice},book_num=#{newNum},type_id=#{typeId}where book_id = #{updateId}</update></mapper>
使用BootStrap渲染數據
下載bootstrap的文件夾和jquery,保存到webapp根目錄下。
由于在web.xml中,SpringMVC的核心類DispatcherServlet(請求分發器)的映射設置成了"/",表示所有請求,包含靜態資源的請求都會交給SpringMVC處理。
解決無法引入靜態資源的問題
-
在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>
創建頂部導航頁面
每個頁面都需要這三句話,為了方便起見,給每個頁面添加頂部導航頁面top.jsp
這樣其他頁面只需要通過<jsp:include>導入該頁面的同時,使用BootStrap的樣式和JS文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>Title</title><%--導入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></head><body><nav class="navbar navbar-default"><div class="container-fluid"><!-- Brand and toggle get grouped for better mobile display --><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="#">Brand</a></div><!-- Collect the nav links, forms, and other content for toggling --><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li><li><a href="#">Link</a></li><li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a><ul class="dropdown-menu"><li><a href="#">Action</a></li><li><a href="#">Another action</a></li><li><a href="#">Something else here</a></li><li role="separator" class="divider"></li><li><a href="#">Separated link</a></li><li role="separator" class="divider"></li><li><a href="#">One more separated link</a></li></ul></li></ul><form class="navbar-form navbar-left"><div class="form-group"><input type="text" class="form-control" placeholder="Search"></div><button type="submit" class="btn btn-default">Submit</button></form><ul class="nav navbar-nav navbar-right"><li><a href="#">Link</a></li><li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a><ul class="dropdown-menu"><li><a href="#">Action</a></li><li><a href="#">Another action</a></li><li><a href="#">Something else here</a></li><li role="separator" class="divider"></li><li><a href="#">Separated link</a></li></ul></li></ul></div><!-- /.navbar-collapse --></div><!-- /.container-fluid --></nav></body>
</html>
列表頁面
<%@ 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>
</head>
<body>
<%--使用jsp動作包含一個子頁面,也能使用其中的css和js文件--%>
<jsp:include page="top.jsp"></jsp:include>
<div class="row" style="width: 100%"><div class="col-md-2"></div><div class="col-md-8"><table class="table-striped table"><tr><th>圖書編號</th><th>類型編號</th><th>圖書名稱</th><th>圖書作者</th><th>圖書價格</th><th>圖書庫存</th><th>出版時間</th><th >操作</th><th><a href="${pageContext.request.contextPath}/bookAdd" class="btn btn-default btn-sm">添加</a></th></tr><c:forEach var="book" items="${list}"><tr><td>${book.bookId}</td><td>${book.typeId}</td><td>${book.bookName}</td><td>${book.bookAuthor}</td><td>${book.bookPrice}</td><td>${book.bookNum}</td><td>${book.publisherDate}</td><td><a href="${pageContext.request.contextPath}/bookInfo/findById?id=${book.bookId}" class="btn btn-primary btn-sm">編輯</a></td><td><a href="${pageContext.request.contextPath}/bookInfo/delete?id=${book.bookId}" class="btn btn-danger btn-sm">刪除</a></td></tr></c:forEach></table></div><div class="col-md-2"></div>
</div>
</body>
</html>
詳情頁面
<%@ 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></head><body><jsp:include page="top.jsp"></jsp:include><div class="row"><div class="col-md-3"></div><div class="col-md-6"><form class="form-horizontal" action="${pageContext.request.contextPath}/bookInfo/update" method="post"><%--隱藏域提交id--%><input type="hidden" name="bookId" value="${book.bookId}"><div class="form-group"><label class="col-sm-2 control-label">圖書名稱</label><div class="col-sm-10"><input type="text" readonly class="form-control" value="${book.bookName}" name="bookName" placeholder="請輸入圖書名稱" required></div></div><div class="form-group"><label class="col-sm-2 control-label">圖書作者</label><div class="col-sm-10"><input type="text" readonly class="form-control" value="${book.bookAuthor}" name="bookAuthor" placeholder="請輸入圖書作者" required></div></div><div class="form-group"><label class="col-sm-2 control-label">圖書類型</label><div class="col-sm-10"><select class="form-control" name="typeId" ><%--遍歷所有的圖書類型--%><c:forEach items="${btList}" var="bt"><option value="${bt.typeId}" ${bt.typeId==book.typeId?"selected":""}>${bt.typeName}</option></c:forEach></select></div></div><div class="form-group"><label class="col-sm-2 control-label">圖書價格</label><div class="col-sm-10"><input type="number" min="1" class="form-control" value="${book.bookPrice}" name="bookPrice" placeholder="請輸入圖書價格" required></div></div><div class="form-group"><label class="col-sm-2 control-label">圖書庫存</label><div class="col-sm-10"><input type="number" min="1" class="form-control" value="${book.bookNum}" name="bookNum" placeholder="請輸入圖書庫存" required></div></div><div class="form-group"><label class="col-sm-2 control-label">出版時間</label><div class="col-sm-10"><input type="date" readonly class="form-control" value="${book.publisherDate}" name="publisherDate" required></div></div><div class="form-group"><div class="col-sm-offset-2 col-sm-10"><button type="submit" class="btn btn-default">修改</button></div></div></form></div><div class="col-md-3"></div></div></body>
</html>
添加頁面
<%--Created by IntelliJ IDEA.User: AdministratorDate: 2023/1/30Time: 14:17To change this template use File | Settings | File Templates.--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>Title</title></head><body><jsp:include page="top.jsp"></jsp:include><div class="row"><div class="col-md-3"></div><div class="col-md-6"><form class="form-horizontal" action="${pageContext.request.contextPath}/bookInfo/insert" method="post"><div class="form-group"><label class="col-sm-2 control-label">圖書名稱</label><div class="col-sm-10"><input type="text" class="form-control" name="bookName" placeholder="請輸入圖書名稱" required></div></div><div class="form-group"><label class="col-sm-2 control-label">圖書作者</label><div class="col-sm-10"><input type="text" class="form-control" name="bookAuthor" placeholder="請輸入圖書作者" required></div></div><div class="form-group"><label class="col-sm-2 control-label">圖書類型</label><div class="col-sm-10"><select class="form-control" name="typeId"></select></div></div><div class="form-group"><label class="col-sm-2 control-label">圖書價格</label><div class="col-sm-10"><input type="number" min="1" class="form-control" name="bookPrice" placeholder="請輸入圖書價格" required></div></div><div class="form-group"><label class="col-sm-2 control-label">圖書庫存</label><div class="col-sm-10"><input type="number" min="1" class="form-control" name="bookNum" placeholder="請輸入圖書庫存" required></div></div><div class="form-group"><label class="col-sm-2 control-label">出版時間</label><div class="col-sm-10"><input type="date" class="form-control" name="publisherDate" required></div></div><div class="form-group"><div class="col-sm-offset-2 col-sm-10"><button type="submit" class="btn btn-default">添加</button></div></div></form></div><div class="col-md-3"></div></div></body><script>/*使用ajax查詢所有圖書類型在頁面中使用ajax訪問后端如果要得到數據,該數據必須是JSON格式*/$.ajax({//請求地址url: "${pageContext.request.contextPath}/bookType/queryAllToJson",//訪問成功后的回調函數success: function (res) {//這里的res是所有類型對象的集合for (var i = 0; i < res.length; i++) {var $opt = $("<option value='" + res[i].typeId + "'>" + res[i].typeName + "</option>")$("select[name=typeId]").append($opt);}}});</script></html>
在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 {@Autowiredprivate 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注解
使用Aajx實現注冊和登錄
表
當前表為customer,包含字段如下
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-qo1GsDU8-1676548026833)(D:/qqFileRecv/框架/框架/SSM03.assets/image-20230131154742977.png)]
使用該表中的phone和password作為登錄時的賬戶和密碼
實體類
package com.hqyj.ssm02.entity;
/*
* 對應customer表
* */
public class SysAdmin {//用戶名adminName屬性對應phone字段private String adminName;//密碼adminPwd屬性對應password字段private String adminPwd;//省略get/set/toString
}
注冊
注冊時先判斷用戶名是否存在,如果存在則不能注冊
-
dao
/*查詢用戶名是否存在,返回查詢到的數量*/ int findByAdminName(String adminName); /*添加*/ int insert(SysAdmin sysAdmin); -
mapper.xml
<!--查詢用戶名(phone)是否存在--> <select id="findByAdminName" resultType="java.lang.Integer">select count(*)from customerwhere phone = #{adminName} </select><!--添加用戶--> <insert id="insert">insert into customervalues (null, #{adminName}, #{adminPwd}, 0, null) </insert> -
service
/** 檢查注冊的用戶名是否存在* */ public boolean findAdminName(SysAdmin sysAdmin) {//查詢要注冊的用戶名是否存在int i = sysAdminDao.findByAdminName(sysAdmin.getAdminName());if (i != 0) {return false;}return true; }/** 注冊* */ public boolean register(SysAdmin sysAdmin) {return sysAdminDao.insert(sysAdmin) > 0; } -
controller
/** 查詢名稱是否存在,返回boolean類型的json字符串* */ @RequestMapping("/findAdminName") @ResponseBody public boolean findAdminName(SysAdmin sysAdmin) {return sysAdminService.findAdminName(sysAdmin); }/** 注冊成功后跳轉到登錄頁面* */ @RequestMapping("/register") public String register(SysAdmin sysAdmin) {sysAdminService.register(sysAdmin);return "login"; } -
頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html><head><title>Title</title><style>.cus-portrait {margin: 0 auto;width: 100px;height: 100px;border: 1px solid #337ab7;text-align: center;line-height: 100px;border-radius: 50px;}</style></head><body><jsp:include page="top.jsp"></jsp:include><div class="col-md-4"></div><div class="col-md-4"><div class="panel panel-primary"><div class="panel-heading">用戶注冊</div><div class="panel-body"><form action="${pageContext.request.contextPath}/sysAdmin/register" method="post"><div class="form-group"><label class="warning">用戶名</label><input type="text" class="form-control" required name="adminName" placeholder="請輸入用戶名"></div><div class="form-group"><label>密碼</label><input type="password" class="form-control" required name="adminPwd" placeholder="請輸入密碼"></div><button type="submit" class="btn btn-default">注冊</button></form></div></div></div><div class="col-md-4"></div><script>$(function () {//用戶名文本框失去焦點觸發$("input[name=adminName]").blur(function () {$.ajax({//訪問controllerurl:"${pageContext.request.contextPath}/sysAdmin/findAdminName",data:{//提交數據"adminName":$(this).val()},success:function(res){//res是true、false。true表示該用戶名不存在if(res){$(".warning").text("√").css("color","green");$("button[type=submit]").removeAttr("disabled");}else{$(".warning").text("該用戶名已存在").css("color","red");$("button[type=submit]").attr("disabled","disabled");}}});});});</script></body> </html>
登錄
-
dao
/*登錄*/ SysAdmin login(SysAdmin sysAdmin);
-
mapper.xml
<!--登錄--> <!--如果查詢的字段名和實體的屬性名不一致,需要自定義查詢結果集映射--> <select id="login" resultMap="loginMap">select *from customerwhere phone = #{adminName}and password = #{adminPwd} </select> <!--自定義返回結果集映射--> <resultMap id="loginMap" type="com.hqyj.ssm02.entity.SysAdmin"><!--表中的phone字段對應SysAdmin對象的adminName字段--><result property="adminName" column="phone"></result><result property="adminPwd" column="password"></result> </resultMap>
-
service
/** 登錄* */ public SysAdmin login(SysAdmin sysAdmin){return sysAdminDao.login(sysAdmin); } -
controller
@RequestMapping("/login") @ResponseBody public SysAdmin login(SysAdmin sysAdmin,Model model) {SysAdmin login = sysAdminService.login(sysAdmin);//將登錄成功的對象保存到session中model.addAttribute("sysAdmin",login);return login; } -
頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html><head><title>Title</title></head><body><jsp:include page="top.jsp"></jsp:include><div class="col-md-4"></div><div class="col-md-4"><div class="panel panel-primary"><div class="panel-heading">用戶登錄</div><div class="panel-body"><div class="form-group"><label>用戶名</label><input type="text" class="form-control" required id="name" placeholder="請輸入用戶名"></div><div class="form-group"><label>密碼</label><input type="password" class="form-control" required id="pwd" placeholder="請輸入密碼"></div><div class="form-group"><label class="warning">驗證碼</label><span class="vcode"></span><input type="text" class="form-control inpVcode" required placeholder="請輸入驗證碼"></div><a class="btn btn-default loginBtn">登錄</a><a class="btn btn-default" href="">注冊</a></div></div></div><div class="col-md-4"></div><script>//1000~9999var vcode = Math.floor(Math.random() * 8999 + 1000);//顯示驗證碼$(".vcode").text(vcode).css("font-weight", "bolder");//定義一個boolean值用于提交數據時判斷驗證碼是否有誤,默認falsevar goon=false;//bind("綁定事件名",滿足時觸發的函數) input propertychange表示監聽文本框輸入事件(只要內容有變化就觸發)$(".inpVcode").bind("input propertychange", function () {if (vcode != $(this).val()) {//如果驗證碼有誤,改變警告文字$(".warning").text("驗證碼輸入錯誤").css("color","#f00");}else{//如果輸入正確,改變boolean值為true$(".warning").text("√").css("color","#0f0");goon=true;}});//登錄按鈕單擊$(".loginBtn").click(function () {//判斷驗證碼if (!goon) {return;}//使用ajax提交數據進行登錄$.ajax({url:"${pageContext.request.contextPath}/sysAdmin/login",data:{"adminName":$("#name").val(),"adminPwd":$("#pwd").val()},success:function (res){if(res!=""){location.href="${pageContext.request.contextPath}/bookInfo/queryAll";}else{alert("用戶名或密碼錯誤");}}});});</script></body> </html>
在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 {@AutowiredSysAdminService sysAdminService;@RequestMapping("/login")@ResponseBodypublic 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";}
}
方式二:HttpSession參數
給項目添加javax.servlet.api依賴,給controller中某個方法添加HttpSession參數后,獲取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 {@AutowiredSysAdminService sysAdminService;@RequestMapping("/login")@ResponseBodypublic SysAdmin login(SysAdmin sysAdmin,HttpSession session) {SysAdmin login = sysAdminService.login(sysAdmin);//將登錄成功的對象保存到session中session.setAttribute("sysAdmin",login);return login;}/*登出時銷毀session*/@RequestMapping("/logout")public String logout(HttpSession session) {session.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 {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String requestURI = request.getRequestURI();//登錄成功后,會在session中保存一個名為sysAdmin的對象Object sysAdmin = request.getSession().getAttribute("sysAdmin");//如果有對象,說明登錄成功,可以放行return trueif(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>
單元測試
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目錄
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-tng8Abg4-1676548026834)(D:/qqFileRecv/框架/框架/SSM04.assets/image-20230201142036858.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-qTD9QJb6-1676548026835)(D:/qqFileRecv/框架/框架/SSM04.assets/image-20230201142055759.png)]
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.Testpublic void fun(){System.out.println("單元測試");}@Autowiredprivate BookInfoDao bookInfoDao;@org.junit.Testpublic void test1(){System.out.println(bookInfoDao.queryAll());}
}
MyBatis整理
使用前提
-
搭建好MyBatis的環境
-
mybatis配置文件模板
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration></configuration>
-
-
根據表創建實體類
-
默認屬性名要和字段名一致
-
如果表中的字段名使用下劃線分割單詞,實體屬性使用駝峰命名法。在mybatis的配置文件中加入開啟駝峰命名方式映射
<!--設置--> <settings><!--開啟駝峰命名映射--><setting name="mapUnderscoreToCamelCase" value="true"/><!--開啟SQL日志--><setting name="logImpl" value="STDOUT_LOGGING"/> </settings> -
如果表中字段名和實體的屬性名既不一致,也不是駝峰命名,就要在寫sql語句時,指定字段名和屬性名的映射關系
-
實體類必須是公共的,其中必須有無參數的構造方法和get/set方法
-
-
數據訪問層
- 數據訪問層通常是一個接口,可以稱為dao層或mapper層,命名時可以是xxDao或xxMapper
-
寫接口的sql映射文件
-
命名通常為xxMapper.xml
-
sql映射文件模板
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="數據訪問層接口的全限定名"> </mapper> -
sql映射文件可以放在resources目錄下,spring的配置文件application.xml中通過"classpath:具體路徑"讀取
如保存在resources/mapper下
<!--SqlSessionFactoryBean--> <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory"><!--指定數據源--><property name="dataSource" ref="druidDataSource"></property><!--讀取mybatis配置文件--><property name="configLocation" value="classpath:mybatis-config.xml"></property><!--讀取sql映射文件--><property name="mapperLocations" value="classpath:mapper/*.xml"></property> </bean> -
sql映射文件也可能放在數據訪問層目錄下,即dao包下
IDEA中,如果將xml文件保存在java包下時,默認在target目錄(真正運行時的目錄)不會編譯加載這些文件,要進行設置
在pom.xml中的build標簽中加入以下內容,重新加載
<!--解決xml如果放在java包下時無法編譯的問題--> <resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource> </resources>spring的配置文件application.xml中讀取路徑正確
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory"><!--指定數據源--><property name="dataSource" ref="druidDataSource"></property><!--讀取mybatis配置文件--><property name="configLocation" value="classpath:mybatis-config.xml"></property><!--讀取sql映射文件--><property name="mapperLocations" value="classpath:com/hqyj/ssm02/dao/mapper/*.xml"></property> </bean>
-
案例
實體類
開啟駝峰命名映射,忽略get/set
BookInfo
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;
}
BookType
public class BookType {private int typeId;private String typeName;
}
查詢<select></select>
單表查詢
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 btwhere 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" ><!--給外鍵對象屬性賦值--><id column="type_id" property="typeId"></id><result column="type_name" property="typeName"></result></association> </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 biinner join book_type bt on bi.type_id = bt.type_idwhere 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_typewhere 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_infowhere type_id = #{typeId} </select>
總結
-
多對一
如果查詢要以從表數據為主,關聯主表相應的數據時,屬于多對一查詢。如查詢所有圖書的同時,顯示其類型。
建議使用子查詢或自定義特殊的sql語句。都需要在從表實體中添加一個主表對象屬性。
-
主表字段比較少,建議使用自定義特殊的sql語句,保證字段重命名為"主表對象.屬性"
-
主表字段比較多,建議使用子查詢,使用
<association>標簽映射主表對象屬性
-
-
一對多
如果查詢要以主表數據為主,關聯該主鍵對應的從表實體對象的集合時,屬于一對多查詢。如查詢某個類型,同時顯示該類型下的所有圖書。
建議使用子查詢。在主表實體中添加一個從表集合屬性。
使用
<collection>標簽映射從表集合屬性
多條件查詢
參考#{}的用法
#{}和${}
在mybatis的sql映射文件中,可以使用#{}和${}表示sql語句中的參數。
#{}相當于預處理,會將參數用?占位后傳值
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-WKsexlaL-1676548026836)(D:/qqFileRecv/框架/框架/SSM04.assets/image-20230201174100891.png)]
${}相當于拼接,會將參數原樣拼接
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ybcWixzA-1676548026836)(D:/qqFileRecv/框架/框架/SSM04.assets/image-20230201174131695.png)]
通常使用#{},防止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
#{}的使用
-
如果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> -
${}的使用
當需要拼接的參數不能帶引號時,必須使用${},如動態表名、排序條件等
動態表名
select * from ${表名}
排序條件
select * from 表名 order by ${字段}
刪除<delete></delete>
刪除單個
delete from 表 where 字段 = 值
只需一個值即可,通常為主鍵id
dao層接口
int delete(int id);
sql映射文件
<delete id="delete">delete from book_info where book_id=#{id}
</delete>
刪除多個
delete from 表 where 字段 in(數據集合)
如delete from book_info where book_id in (1001,1002,1005)
in后面的內容通常是一個數組
前端頁面通過復選框選中要刪除的數據,獲取選中的數據的id,保存到一個數組中
dao層接口
int deleteByIds(@Param("idList")List<Integer> idList)
sql映射文件
<delete id="deleteByIds">delete from book_info where book_id in <!-- foreach標簽用戶遍歷dao層傳遞的集合對象collection表示要遍歷的集合open和close表示將遍歷出的數據使用什么開頭和結尾separator表示將遍歷出的數據用什么分隔--><foreach collection="idList" item="id" separator="," open="(" close=")">#{id}</foreach>
</delete>
foreach標簽可以用于sql語句中條件是集合的情況,配合where條件使用,dao層接口中方法參數為集合。
如where 字段 in/not in (值1,值2,值3)
添加<insert></insert>
添加時的參數雖多,但通常為一個完整的實體對象,所以dao層接口中方法的參數要定義成一個實體對象。
dao層
int insert(BookType bookType);
sql映射文件
- 通常加上parameterType參數指定添加的對象實體類全限定名
- #{}中的參數一定來自于dao層接口方法實體參數對象的屬性
- 如果要在添加成功的同時,獲取自動增長的主鍵值時,要添加
- useGeneratedKeys=“true” 表示自動獲取自增的值
- keyColumn=“type_id” 表示自增字段
- keyProperty=“typeId” 表示自增字段對應的屬性名
<insert id="testInsert" useGeneratedKeys="true" keyColumn="type_id" keyProperty="typeId"parameterType="com.hqyj.ssm02.entity.BookType">insert into book_typevalues (null, #{typeName})
</insert>
修改<update></update>
修改可以分為修改所有字段和修改部分字段
修改所有字段
dao層
int update(BookInfo bookInfo);
sql映射文件
<update id="update">update book_info setbook_name = #{bookName},book_author = #{bookAuthor},book_num = #{bookNum},book_price = #{bookPrice},publisher_date = #{publisherDate}where book_id=#{bookId}
</update>
這種方式會修改所有字段,如果實體參數的某個屬性沒有賦值,就會用該屬性的默認值進行修改。
如String類型的屬性沒有賦值,就會用null修改,int類型用0修改,如果表中該字段非空,就會導致sql執行異常。
所以修改所有字段,參數為一個完整對象時,保證其中的屬性都有值。
修改部分字段
方式一:dao層只寫要修改的字段
dao
int updateSth(int bookId,int bookPrice,int bookNum);
sql映射文件
<update id="updateSth">update book_info setbook_num = #{bookNum},book_price = #{bookPrice}where book_id = #{bookId}
</update>
方式二:dao層寫完整對象,sql語句中判斷字段是否有值
dao
int updateSth(BookInfo bookInfo);
sql映射文件
<update id="updateSth">update book_info set<if test="bookPrice!=0">book_price = #{bookPrice},</if><if test="bookName!=null">book_name=#{bookName}</if>where book_id = #{bookId}
</update>
這樣寫,有以下幾個問題
1.如果只有第一個條件滿足,后續條件都不滿足,最終拼接的sql語句,會在where關鍵字之前多出一個逗號,導致語法錯誤
解決方式:將set替換成<set>標簽,mybatis會自動去除最后的逗號
2.替換為<set>標簽后,如果所有條件都不滿足,mybatis會自動去除set部分,sql語句就會變為update book_info where book_id=?,導致語法錯誤
解決方式:在<set>標簽中,添加一個不影響原始數據的條件,如 book_id = #{bookId},只需要一個book_id參數,sql語句沒有語法錯誤,對原始數據沒有任何影響
動態SQL
-
<set>搭配<if>用于修改<!--動態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> -
<where>搭配<if>用于查詢、修改、刪除時的條件<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> -
<trim>搭配<if>可以替換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 = [];//獲取當前被選中的復選框所在的trlet $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";
}
多條件查詢具體實現
搜索表單
<form class="navbar-form navbar-left"action="${pageContext.request.contextPath}/bookInfo/queryByCondition"><div class="form-group"><input type="text" class="form-control" name="bookName" placeholder="請輸入書名關鍵字"><input type="text" class="form-control" name="bookAuthor" placeholder="請輸入作者關鍵字"><select id="topSelect" class="form-control" name="typeId"><option value="0">全部</option></select></div><button type="submit" class="btn btn-default">搜索</button>
</form>
dao
List<BookInfo> queryByCondition(BookInfo bookInfo);
mapper.xml
<select id="queryByCondition" resultType="com.hqyj.ssm02.entity.BookInfo">SELECT bi.*,type_name as 'bookType.typeName' FROM book_info bi,book_type bt<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>
service
public List<BookInfo> queryByCondition(BookInfo bookInfo) {return bookInfoDao.queryByCondition(bookInfo);
}
controller
@RequestMapping("/queryByCondition")
public String queryByCondition(BookInfo bookInfo,Model model) {List<BookInfo> list= bookInfoService.queryByCondition(bookInfo);model.addAttribute("list",list);return "bookInfoList";
}
分頁
使用分頁組件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.isHasNextPage();
System.out.println("是否是首頁"+pageInfo.isIsFirstPage());
System.out.println("是否是尾頁"+pageInfo.isIsLastPage());
| PageInfo對象常用屬性和方法 | 作用 |
|---|---|
| total/getTotal() | 得到總記錄數 |
| pages/getPages() | 得到最大頁數 |
| pageNum/getPageNum() | 得到當前頁 |
| size/getSize() | 得到每頁顯示的記錄數 |
| list/getList() | 得到按當前page和size查詢到的數據集合 |
| isFirstPage/isIsFirstPage() | 是否是首頁 |
| isLastPage/isIsLastPage() | 是否是尾頁 |
多條件分頁具體實現
controller
@RequestMapping("/queryByCondition")
public String queryByCondition(@RequestParam(defaultValue = "1") int pageNum,@RequestParam(defaultValue = "8") int size,BookInfo bookInfo,Model model) {//1.PageHelper.startPage()PageHelper.startPage(pageNum, size);//2.查詢集合List<BookInfo> list = bookInfoService.queryByCondition(bookInfo);//3.PageInfo(集合)PageInfo<BookInfo> pageInfo = new PageInfo<>(list);//將查詢到的分頁模型對象保存到model中model.addAttribute("pageInfo",pageInfo);//構造頁數的集合ArrayList<Integer> pageList = new ArrayList<>();for (int i = 1; i <= pageInfo.getPages(); i++) {pageList.add(i);}model.addAttribute("pageList",pageList);return "bookInfoList";
}
頁面分頁組件
<nav aria-label="Page navigation"><ul class="pagination"><c:if test="${!pageInfo.isFirstPage}"><li><a href="${pageContext.request.contextPath}/bookInfo/queryByCondition?bookName=${param.bookName}&bookAuthor=${param.bookAuthor}&typeId=${param.typeId}&pageNum=${pageInfo.pageNum-1}"aria-label="Previous"><span aria-hidden="true">«</span></a></li></c:if><c:forEach items="${pageList}" var="pno"><li class="pno"><a href="${pageContext.request.contextPath}/bookInfo/queryByCondition?bookName=${param.bookName}&bookAuthor=${param.bookAuthor}&typeId=${param.typeId}&pageNum=${pno}">${pno}</a></li></c:forEach><c:if test="${!pageInfo.isLastPage}"><li><a href="${pageContext.request.contextPath}/bookInfo/queryByCondition?bookName=${param.bookName}&bookAuthor=${param.bookAuthor}&typeId=${param.typeId}&pageNum=${pageInfo.pageNum+1}"aria-label="Next"><span aria-hidden="true">»</span></a></li></c:if></ul>
</nav>
SpringBoot
Spring推出的一個Spring框架的腳手架。
不是一個新的框架,而是搭建Spring相關內容框架的平臺。
它省去了Spring、SpringMVC項目繁瑣的配置過程,讓開發變得更加簡單。
本質還是Spring+SpringMVC,可以搭配其他的ORM框架,如MyBatis、MyBatisPlus、JPA、Hibernate等。
特點
- 內置了Tomcat服務器,不需要部署項目到Tomcat中
- 內置了數據源Hikari
- 減少了jar文件依賴的配置
- SpringBoot中的配置文件可以使用yml格式文件,代替properties或xml
創建SpringBoot項目
通過IDEA創建
通過官網模板創建
官網模板https://start.spring.io/
點擊生成,會下載一個壓縮文件,解壓后通過IDEA打開。
無論哪種方式,都需要重寫設置Maven配置文件
創建成功后的目錄結構
第一個springboot項目的helloworld
熱部署
項目在開發過程中,可以不需要每次都重啟,等待一段時間后會自動更新編譯運行
使用
添加依賴,可以在創建的項目的時候選擇,也可以中途添加
<!--熱部署-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><version>2.7.8</version>
</dependency>
開啟熱部署
Lombok
用于簡化實體類中模板代碼的工具
使用
添加依賴,可以在創建的項目的時候選擇,也可以中途添加
<!--Lombok-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version><scope>provided</scope>
</dependency>
安裝插件(IDEA2020.2之后的版本會內置Lombok插件,無需安裝)
-
IDEA插件官網Versions: Lombok - IntelliJ IDEs Plugin | Marketplace (jetbrains.com)
-
IDEA內置插件市場搜索
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-xEZQPWWk-1676548026840)(D:/qqFileRecv/框架/框架/day10-SpringBoot+MyBatis1.assets/image-20230203140602578.png)]
在某個實體類上添加注解
| lombok常用注解 | 作用 |
|---|---|
| @AllArgsConstructor | 自動生成全參構造方法 |
| @Data | 以下注解之和 |
| @Setter | 自動生成set方法 |
| @Getter | 自動生成get方法 |
| @NoArgsConstructor | 自動生成無參構造方法 |
| @ToString | 自動生成toString方法 |
| @EqualsAndHashcode | 自動生成equals和hashcode方法 |
SpringBoot+MyBatis實現單表查詢
1.創建好SpringBoot項目
最好在創建的時候選擇以下依賴
- spring-web(必選)
- lombok
- spring-devtools
- springboot集成mybatis
- mysql驅動
都可以后續添加
<!--web-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><!--熱部署-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><version>2.7.8</version>
</dependency><!--Lombok-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version><scope>provided</scope>
</dependency>
2.在pom.xml中添加mybatis集成SpringBoot依賴和數據庫驅動
<!--mysql-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.31</version>
</dependency>
<!--springboot集成MyBatis-->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.0</version>
</dependency>
3.在springboot配置文件application.properties中
#注釋
#.properties文件稱為屬性文件,數據以鍵值對"鍵=值"的形式保存
#設置項目啟動端口號
#server.port=9090
#設置項目上下文路徑
#server.servlet.context-path=/springbootday1#mybatis相關配置
#開啟駝峰命名映射
mybatis.configuration.map-underscore-to-camel-case=true
#打印sql語句
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#掃描mybatis的sql映射文件(將mapper.xml文件保存在resources目錄下的mapper目錄下)
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=root
mybatis的sql映射文件模板
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--設置該文件對應的dao層接口全限定名-->
<mapper namespace=""></mapper>
4.根據數據表創建實體類、dao層接口、service、controller
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-H1SwbwcE-1676548026840)(D:/qqFileRecv/框架/框架/day10-SpringBoot+MyBatis1.assets/image-20230203154807705.png)]
Hero
@Data
public class Hero{private Integer id;private String name;private String position;private String sex;private Double price;private String shelfDate;
}
dao
@Repository
public interface HeroDao{List<Hero> queryAll();
}
mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--設置該文件對應的dao層接口全限定名-->
<mapper namespace="com.xxx.dao.HeroDao"><select id="queryAll" resultType="com.xxx.entity.Hero">select * from hero</select>
</mapper>
service
@Service
public class HeroService{@Autowiredprivate HeroDao heroDao;public List<Hero> queryAll(){return heroDao.queryAll(); }
}
controller
@Controller
@RequestMapping("/hero")
public class HeroController{@Autowiredprivate HeroService heroService;@RequestMapping("/queryAll")@ResponseBodypublic List<Hero> queryAll(){return heroDao.queryAll(); }
}
5.在SpringBoot的啟動類上,加入@MapperScan注解,掃描dao層所在根包
@SpringBootApplication
//掃描dao層所在的根包
@MapperScan("com.hqyj.first.dao")
public class FirstApplication {public static void main(String[] args) {SpringApplication.run(FirstApplication.class, args);}}
啟動項目,按自定的項目上下文路徑和端口號訪問某個controller中的方法
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-3V572UD0-1676548026841)(D:/qqFileRecv/框架/框架/day10-SpringBoot+MyBatis1.assets/image-20230203155852438.png)]
SpringBoot+LayUI實現酒店客房管理
核心知識點
-
SpringBoot項目搭建
-
核心依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency> -
集成MyBatis
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.0</version> </dependency>
-
-
新注解
-
@RestController
如果某個控制器類中的所有方法都要加@ResponseBody,可以在類上加@ResponseBody,也可以用@RestController代替@ResponseBody和@Controller
-
-
LayUI
- 數據表格
- 數據接口格式
- 真假分頁
- 頭工具欄事件
- 行內事件
- 單元格編輯事件
- 彈出層
- layer.msg()
- layer.confirm()
- layer.open()
- 數據表格
-
ajax
$.ajax({url:"",data:xxx,type:"get/post",success:function(){} }) -
前后端分離
- 該案例可以將頁面獨立出來,成為一個前后端分離項目,也可以將頁面作為靜態資源保存在static目錄下
-
如果設計為前后端分離,要在控制器類上加入@CrossOrigin,表示該類中的所有方法允許跨域請求
-
打包SpringBoot項目
保證項目中無錯誤,包含單元測試中
? 打包后是一個.jar文件,位于target目錄中
? 在安裝有java環境的機器中,控制臺運行jar文件
java -jar 文件名.jar
數據庫部分設計
數據庫表詳細設計
核心Java代碼
實體類
訂單表
/*
* 訂單
* */
@Data
public class Orders {private Integer id;private Integer roomNo;private Integer cusId;//@JsonFormat是springboot集成的jackson包中的注解,用于格式化日期//pattern指定格式日期 timezone指定時區,這里和數據庫的時區保持一致@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "Asia/Shanghai")private Date leaveTime;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "Asia/Shanghai")private Date stayTime;private Integer cost;//顯示訂單的同時,要顯示房間的相關信息private Room room;//顯示訂單的同時,要顯示客戶的相關信息private Customer customer;
}
數據訪問層
-
實現隨意修改某個字段的值
int update(@Param("field") String field,@Param("value") String value,@Param("id") int id);sql映射文件
<update id="update">update room<set><if test="field=='roomType'">room_type= #{value}</if><if test="field=='roomPrice'">room_price= #{value}</if><if test="field=='roomUse'">room_use=#{value}</if></set>where room_no=#{id} </update> -
添加時獲取自增的值
int insert(Customer customer);sql映射文件
<!--添加顧客的同時,獲取自增的id--> <insert id="insert" useGeneratedKeys="true" keyColumn="cus_id" keyProperty="cusId">insert into customer values(null,#{cusName},#{cusPhone},#{cusIdcard}) </insert> -
多表連接查詢/條件查詢
List<Orders> queryAll();List<Orders> search(String keyword);//根據客戶編號查詢是否已入住 Orders isStay(Integer cusId);//根據房間號查詢正在入住的訂單信息 Orders findCheckInByRoomNo(Integer roomNo);//計算費用 int checkOut(@Param("roomNo") Integer roomNo,@Param("cost") Integer cost);sql映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--設置該文件對應的dao層接口全限定名--> <mapper namespace="com.hqyj.hotel_sys.dao.OrdersDao"><insert id="insert">insert into ordersvalues (null, #{roomNo}, #{cusId}, now(), null, 0)</insert><!--關聯查詢方式一:特殊的SQL語句--><!--<select id="queryAll" resultType="com.hqyj.hotel_sys.entity.Orders">select o.*,cus_name as 'customer.cusName',cus_phone as 'customer.cusPhone',cus_idcard as 'customer.cusIdcard',room_type as 'room.roomType',room_price as 'room.roomPrice',room_use as 'room.roomUse'from customer c,room r,orders owhere c.cus_id = o.cus_idand r.room_no = o.room_no</select>--><!--多表連接條件查詢--><select id="search" resultMap="ordersMap">SELECT * FROM orders o,customer c<trim prefix="where" prefixOverrides="and">o.cus_id = c.cus_id<if test="keyword!=null">and cus_name like concat('%',#{keyword},'%')</if></trim></select><!--關聯查詢方式二:子查詢--><!--1.查詢自身表--><select id="queryAll" resultMap="ordersMap">select *from orders</select><!--自定義結果集映射,用cus_id和room_no做子查詢,重新映射一次到orders對象--><resultMap id="ordersMap" type="com.hqyj.hotel_sys.entity.Orders"><result property="roomNo" column="room_no"></result><result property="cusId" column="cus_id"></result><association property="room" column="room_no" select="findRoomByNo"></association><association property="customer" column="cus_id" select="findCustomerById"></association></resultMap><!--子查詢一:根據房號查房間--><select id="findRoomByNo" resultType="com.hqyj.hotel_sys.entity.Room">select *from roomwhere room_no = #{roomNo}</select><!--子查詢二:根據編號查客戶--><select id="findCustomerById" resultType="com.hqyj.hotel_sys.entity.Customer">select *from customerwhere cus_id = #{cusId}</select><select id="isStay" resultType="com.hqyj.hotel_sys.entity.Orders">select *from orderswhere cus_id = #{cusId}and leave_time is null</select><!--根據房號查詢正在入住的訂單信息,包含房間價格--><select id="findCheckInByRoomNo" resultType="com.hqyj.hotel_sys.entity.Orders">select o.*, room_price as 'room.roomPrice'from orders o,room rwhere o.room_no = r.room_noand r.room_no = #{roomNo}and leave_time is null</select><update id="checkOut">update ordersset leave_time=now(),cost=#{cost}where room_no = #{roomNo}and leave_time is null</update> </mapper>
業務流程層
package com.hqyj.hotel_sys.service;import com.hqyj.hotel_sys.dao.CustomerDao;
import com.hqyj.hotel_sys.dao.OrdersDao;
import com.hqyj.hotel_sys.dao.RoomDao;
import com.hqyj.hotel_sys.entity.Customer;
import com.hqyj.hotel_sys.entity.Orders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.Date;
import java.util.List;@Service
public class CustomerService {@Autowiredprivate CustomerDao customerDao;@Autowiredprivate RoomDao roomDao;@Autowiredprivate OrdersDao ordersDao;/** 入住* 1.獲取顧客信息:姓名、電話、身份證號,* 根據身份證號判斷是否存在,如果存在,用存在的對象* 如果不存在,向customer表中添加一條記錄 insert into customer values(null,#{cusName},#{cusPhone},#{cusIdcard})* 添加成功時,要獲取自增的id,用于訂單表中使用* 判斷該顧客是否已入住:查詢訂單表中對應顧客編號的記錄,如果退房時間為空,說明已入住* select * from orders where cus_id=#{cusId} and leave_time is null* 2.將對應房間的狀態改為1,對room表中修改 update room set room_use=1 where room_no=#{roomNo}* 3.向訂單表中添加一條記錄 insert into orders values (null,#{roomNo},#{cusId},now(),null,0)** 以上3個步驟屬于一個事務,要在該方法上加事務注解* */@Transactional//讓該方法成為一個事務,如果執行中途出錯,會自動回滾public boolean checkIn(Customer customer, int roomNo) {//1.添加客戶,根據身份證號判斷是否存在Customer byIdcard = customerDao.findByIdcard(customer.getCusIdcard());boolean b1;if (byIdcard == null) {//不存在,調用添加b1 = customerDao.insert(customer) > 0;} else {customer = byIdcard;b1 = true;}//查看是否已入住if (ordersDao.isStay(customer.getCusId()) != null) {return false;}//2.修改房間狀態boolean b2 = roomDao.update("roomUse", "1", roomNo) > 0;//3.添加訂單信息//創建訂單對象Orders orders = new Orders();//客戶編號在添加客戶成功后,會自動獲取orders.setCusId(customer.getCusId());orders.setRoomNo(roomNo);boolean b3 = ordersDao.insert(orders) > 0;return b1 & b2 & b3;}/** 退房* 1.根據房間號查詢對應正在入住的訂單記錄(同時查詢出房價)* select o.* from orders o,room r where o.room_no=r.room_no and r.room_no =#{roomNo} and leave_time is null* 2.結算* 添加退房時間、添加花費* update orders set leave_time =now() ,cost=#{cost} where room_no =#{roomNo} and leave_time is null* 3.修改房間狀態為空閑* update room set room_use = 0 where room_no=#{roomNo}* */@Transactionalpublic boolean checkOut(Integer roomNo) {//1.根據房間號查詢對應正在入住的訂單記錄Orders orders = ordersDao.findCheckInByRoomNo(roomNo);//2.計算費用Date stayTime = orders.getStayTime();Date now = new Date();double l = now.getTime() - stayTime.getTime();//轉換為天數double day = Math.ceil(l / 1000 / 3600 / 24);double cost = orders.getRoom().getRoomPrice() * day;//修改訂單中的花費和退房時間//update orders set leave_time=now(),cost=#{cost} where room_no=#{roomNo} and leave_time is nullboolean b1 = ordersDao.checkOut(roomNo, (int) cost) > 0;//3.修改房間狀態為空閑0//update room set room_use where room_no = #{roomNo}boolean b2 = roomDao.update("roomUse", "0", roomNo) > 0;return b1 & b2;}
}
控制層
如果某個控制器中的所有方法都需要返回JSON格式字符串,在類上加@ResponseBody
@Controller
@RequestMapping("/room")
public class RoomController {@Autowiredprivate RoomService roomService;@ResponseBody@RequestMapping("/queryAll")public ResultData<Room> queryAll(Integer page, Integer limit) {//使用PageHelper分頁PageHelper.startPage(page, limit);//正常查詢所有List<Room> rooms = roomService.queryAll();//創建分頁模型對象PageInfo<Room> pageInfo = new PageInfo<>(rooms);//返回的數據為分頁后的集合,數量為總記錄數return new ResultData<>((int)pageInfo.getTotal(),pageInfo.getList());}
}
LayUI數據表格
數據表格所需的數據接口格式為
{code:0,msg:"",count:1000,data:[{},{}]
}
構造滿足LayUI數據表格的數據模型類ResultData
/*
* 定義一個用于LayUI數據表格對應格式的模板類
* code 狀態碼 0成功
* msg 提示文字
* count 數據總量
* data 數據集合
* */
@Data
public class ResultData<T> {private Integer code;private String msg;private Integer count;private List<T> data;/** 定義帶count和data的構造方法,用于初始化code和msg* */public ResultData(Integer count, List<T> data) {code=0;msg="";this.count = count;this.data = data;}
}
最后在控制層中,將查詢的方法的返回值更改為ResultData類型
@RequestMapping("/queryAll")
public ResultData<Room> queryAll() {List<Room> rooms = roomService.queryAll();return new ResultData<>(rooms.size(), rooms);
}
最終頁面
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>LayUI</title><meta name="renderer" content="webkit"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"><link rel="stylesheet" href="../LayUI/css/LayUI.css" media="all"><!-- 注意:如果你直接復制所有代碼到本地,上述css路徑需要改成你本地的 -->
</head>
<body>
<table class="LayUI-hide" id="test" lay-filter="test"></table>
<script type="text/html" id="toolbarDemo"><div class="LayUI-btn-container"><button class="LayUI-btn LayUI-btn-sm" lay-event="getCheckData">獲取選中行數據</button><button class="LayUI-btn LayUI-btn-sm" lay-event="getCheckLength">獲取選中數目</button><button class="LayUI-btn LayUI-btn-sm" lay-event="isAll">驗證是否全選</button><button class="LayUI-btn LayUI-btn-sm" lay-event="addRoom">添加客房</button></div>
</script>
<script type="text/html" id="barDemo"><!--插值表達式-->{{# if(d.roomUse==0){ }}<a class="LayUI-btn LayUI-btn-normal LayUI-btn-sm" lay-event="check-in">入住</a><a class="LayUI-btn LayUI-btn-danger LayUI-btn-sm" lay-event="del">刪除</a>{{# }else{ }}<a class="LayUI-btn LayUI-btn-warm LayUI-btn-sm" lay-event="check-out">退房</a>{{# } }}
</script><script src="../LayUI/LayUI.js" charset="utf-8"></script>
<!-- 注意:如果你直接復制所有代碼到本地,上述 JS 路徑需要改成你本地的 -->
<script>LayUI.use('table', function () {var table = LayUI.table;//引入Jqueryvar $ = LayUI.$;/** 渲染表格數據* url:數據訪問接口* cols:表格列* field:數據字段名* title:表頭* cellminwidth:100 自適應寬度* fixed:left 固定在某側* unresize:true 不可改變尺寸* sort:true 排序* edit:true 行內編輯* */table.render({elem: '#test'//設置數據接口, url: 'http://localhost:9090/hotel/room/queryAll', toolbar: '#toolbarDemo' //開啟頭部工具欄,并為其綁定左側模板, defaultToolbar: ['filter', 'exports', 'print', { //自定義頭部工具欄右側圖標。如無需自定義,去除該參數即可title: '提示', layEvent: 'LAYTABLE_TIPS', icon: 'LayUI-icon-tips'}], title: '用戶數據表', cols: [[{type: 'checkbox', fixed: 'left'}, {field: 'roomNo', title: '房間號', cellminwidth: 100, fixed: 'left', unresize: true, sort: true}, {field: 'roomType', title: '房間類型', cellminwidth: 100, edit: 'text'}, {field: 'roomPrice', title: '房間單價', cellminwidth: 100, edit: 'text', sort: true}, {field: 'roomUse', title: '使用狀態', cellminwidth: 100,templet: function (res) {return res.roomUse == 0 ? "<span style='color:green'>空閑中</span>" : "<span style='color:red'>使用中</span>";}}, {fixed: 'right', title: '操作', toolbar: '#barDemo', cellminwidth: 100,}]]//開啟分頁組件, page: true//設置每頁顯示記錄數的下拉選項, limits: [5, 10, 20]//默認每頁顯示的條數,沒有設置默認為10, limit: 10//解析數據表格url請求后的數據, parseData: function (res) {//res就是url對應的數據/*//假分頁var result;if (this.page.curr) {result = res.data.slice(this.limit * (this.page.curr - 1), this.limit * this.page.curr);} else {result = res.data.slice(0, this.limit);}*/return {"code": res.code,"msg": res.msg,"count": res.count,"data": res.data}}});//頭工具欄事件table.on('toolbar(test)', function (obj) {var checkStatus = table.checkStatus(obj.config.id);switch (obj.event) {case 'addRoom':layer.open({title: '添加客房',type: 2,content: 'addRoom.html',area: ['350px', '250px'],resize: false,anim: 2,/* success: function(layero, index){console.log(layero, index);}*/})break;case 'getCheckData'://data是所選數據的集合var data = checkStatus.data;for (var i = 0; i < data.length; i++) {console.log(data[i].roomNo)}// layer.alert(JSON.stringify(data));break;case 'getCheckLength':var data = checkStatus.data;layer.msg('選中了:' + data.length + ' 個');break;case 'isAll':layer.msg(checkStatus.isAll ? '全選' : '未全選');break;//自定義頭工具欄右側圖標 - 提示case 'LAYTABLE_TIPS':layer.alert('這是工具欄右側自定義的一個圖標按鈕');break;};});//監聽單元格編輯事件table.on('edit(test)', function (obj) {// console.log(obj);//layer.confirm("提示文件",function(){確認觸發},function(){取消觸發})layer.confirm('確認要修改嗎', function (index) {//使用ajax提交要修改的字段名、修改后的值、要修改的編號$.ajax({url: "http://localhost:9090/hotel/room/update",data: {"field": obj.field,//要修改的字段"value": obj.value,//修改后的值"id": obj.data.roomNo//要修改的id},success: function () {//修改成功,關閉確認框layer.close(index);}});}, function () {//修改失敗,重新加載location.reload()})})//監聽行工具事件table.on('tool(test)', function (obj) {//data就是當前行中的數據var data = obj.data;// console.log(obj)if (obj.event === 'del') {//彈出確認框layer.confirm('真的刪除行么', function (index) {//使用ajax提交要刪除的id$.ajax({url: "http://localhost:9090/hotel/room/delete",data: {"delId": data.roomNo},success: function (res) {if (res) {//前端假刪除obj.del();//關閉確認框layer.close(index);}}});});} else if (obj.event === 'check-in') {//彈出輸入顧客信息表單layer.open({title: '輸入顧客信息',type: 2,content: 'addCustomer.html',area: ['350px', '400px'],resize: false,anim: 2,//彈出窗口后的回調函數success: function (layero, index) {//彈出成功后,在彈出頁面中加入當前點擊行的roomNo//獲取彈出層的body部分var body = layer.getChildFrame('body', index);//獲取彈出層body中的隱藏域,給其賦值body.find("input[name=roomNo]").val(data.roomNo);}})} else if (obj.event === 'check-out') {layer.confirm("確定要退房嗎", function () {$.ajax({url: "http://localhost:9090/hotel/customer/checkOut",data: {"roomNo": data.roomNo},success: function (res) {if (res) {location.reload();}}});})}});});
</script></body>
</html>
LayUI添加頁面
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="../LayUI/css/LayUI.css" media="all"></head><body><form class="LayUI-form" action=""><div class="LayUI-form-item"><label class="LayUI-form-label">房間類型</label><div class="LayUI-input-inline"><input type="text" name="roomType" required lay-verify="required" placeholder="請輸入房間類型" autocomplete="off" class="LayUI-input"></div></div><div class="LayUI-form-item"><label class="LayUI-form-label">房間單價</label><div class="LayUI-input-inline"><input type="text" name="roomPrice" required lay-verify="required" placeholder="請輸入房間單價" autocomplete="off" class="LayUI-input"></div></div><div class="LayUI-form-item"><div class="LayUI-input-block"><button class="LayUI-btn" lay-submit lay-filter="formDemo">立即提交</button><button type="reset" class="LayUI-btn LayUI-btn-primary">重置</button></div></div></form><script src="../LayUI/LayUI.js" charset="utf-8"></script><script>//DemoLayUI.use('form', function(){var form = LayUI.form;//使用LayUI內置jqueryvar $=LayUI.$;//監聽提交form.on('submit(formDemo)', function(data){// data.field是當前表單中的所有數據// layer.msg(JSON.stringify(data.field));$.ajax({url:'http://localhost:9090/hotel/room/addRoom',//將表單中的所有數據一起提交,實際提交的是name=value,如roomType=值&roomPrice=值data:data.field,//相當于/* data:{roomType:"",roomPrice:""}*/success:function(res){if(res){//刷新父頁面parent.location.reload()}}})//return false時不提交表單return false;});});</script></body>
</html>
LayUI分頁
-
假分頁
查詢所有,在頁面中分頁,適合記錄比較少的情況
table.render({//省略url等//開啟分頁組件, page: true//設置每頁顯示記錄數的下拉選項, limits: [5, 10, 20]//默認每頁顯示的條數,沒有設置默認為10, limit: 10//解析數據表格url請求后的數據, parseData: function (res) {//res就是url對應的數據//假分頁var result;if (this.page.curr) {result = res.data.slice(this.limit * (this.page.curr - 1), this.limit * this.page.curr);} else {result = res.data.slice(0, this.limit);}return {"code": res.code,"msg": res.msg,"count": res.count,"data": result}} }) -
真分頁
可以使用PageHelper組件
依賴
<!--分頁組件SpringBoot集成PageHelper--> <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.6</version> </dependency>在application.properties中配置
# 防止不合理分頁 pagehelper.reasonable=true在控制層中使用
@ResponseBody @RequestMapping("/queryAll") //這里的page和limit參數是layui數據表格自動傳遞 public ResultData<Room> queryAll(Integer page, Integer limit) {//使用PageHelper分頁PageHelper.startPage(page, limit);//正常查詢所有List<Room> rooms = roomService.queryAll();//創建分頁模型對象PageInfo<Room> pageInfo = new PageInfo<>(rooms);//返回的數據為分頁后的集合,數量為總記錄數return new ResultData<>((int)pageInfo.getTotal(),pageInfo.getList()); }
LayUI條件查詢
頁面頭部工具欄中加入搜索框
<script type="text/html" id="toolbarDemo"><div class="layui-form-item"><div class="layui-input-inline"><input type="text" name="keyword" required lay-verify="required" placeholder="請輸入姓名關鍵字" autocomplete="off"class="layui-input"></div><button class="layui-btn layui-btn-primary" lay-event="search">搜索</button></div>
</script>
js部分
//頭工具欄事件
table.on('toolbar(test)', function (obj) {switch (obj.event) {case 'search'://獲取輸入的內容let keyword = $("input[name=keyword]").val();if(keyword==""){layer.msg("輸入不能為空");return;}//如果要修改數據表格中的數據,只能更改url的地址后,重新加載數據表格table.reload('test',{url:"http://localhost:9090/hotel/orders/search?keyword="+keyword});break;}
});
dao層參考上方數據訪問層條件查詢部分
MyBatisPlus
官網簡介 | MyBatis-Plus (baomidou.com)
MyBatis-Plus (簡稱 MP)是一個MyBatis的增強工具,在 MyBatis 的基礎上只做增強不做改變,為簡化開發、提高效率而生。
只需簡單的配置,就能實現對單表的CURD。
其核心有兩個接口:BaseMapper和IService
BaseMapper中封裝了大量數據訪問層的方法
IServcie中封裝了大量業務流程層的方法
SpringBoot+MyBatisPlus
1.創建SpringBoot項目
創建時勾選依賴
- devtools
- lombok
- spring-web
- mysql-driver
2.導入SpringBoot集成MyBatisPlus依賴
<!-- 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=root# 開啟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 = “字段名”)**
- 主鍵字段對應的屬性,需要加入@TableId注解,其type屬性表示主鍵生成策略
- @TableId(type = IdType.AUTO)表示主鍵自增,在數據庫也要將主鍵設置為自增
- @TableId(type = IdType.ASSIGN_ID)//IdType.ASSIGN_ID表示使用"雪花算法"(根據時間和機器特征碼)生成一個id
- @TableId(type = IdType.ASSIGN_UUID)//IdType.ASSIGN_UUID表示使用UUID生成一個隨機字符串id
@Data
public class Hero {//type表示主鍵生成策略,@TableId(type = IdType.AUTO)// IdType.AUTO表示主鍵自增,在數據庫也要將主鍵設置為自增//@TableId(type = IdType.ASSIGN_ID)//IdType.ASSIGN_ID表示使用"雪花算法"(根據時間和機器特征碼)生成一個id//@TableId(type = IdType.ASSIGN_UUID)//IdType.ASSIGN_UUID表示使用UUID生成一個隨機字符串idprivate Integer id;//如果屬性名和字段名不一致@TableField(value = "name")private String heroName;private String position;private String sex;private Integer price;private String shelfDate;
}
5.編寫數據訪問層接口
可以不用寫@Repository,繼承BaseMapper接口,設置泛型
/*
* 數據訪問層可以稱為dao或mapper層
* 可以不用加@Repository注解
* */
public interface HeroMapper extends BaseMapper<Hero> {}
6.在SpringBoot的啟動類中,掃描數據訪問層所在包
@SpringBootApplication
@MapperScan("com.hqyj.sbmp01.mapper")
public class Sbmp01Application {public static void main(String[] args) {SpringApplication.run(Sbmp01Application.class, args);}
}
測試
在SpringBoot自帶的單元測試類中,注入HeroMapper對象,調用BaseMapper中定義的方法即可實現CURD。
BaseMapper接口
BaseMapper接口中定義了常用的增刪改查方法,
在數據訪問層接口中繼承該接口
方法列表
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-PJYyGHZA-1676548026845)(D:/qqFileRecv/框架/框架/SpringBoot+MyBatisPlus.assets/image-20230208172250774.png)]
使用
public interface HeroMapper extends BaseMapper<Hero> {}
BaseMapper接口中的常用方法
| 方法名 | 參數 | 作用 |
|---|---|---|
| selectList(Wrapper wrapper) | 條件構造器 | 根據條件查詢集合,如果實參為null表示查詢所有,返回List集合 |
| selectById(Serializable id) | 主鍵 | 根據主鍵查詢單個對象,返回單個對象 |
| selectOne(Wrapper wrapper) | 條件構造器 | 條件查詢單個對象,返回單個對象 |
| insert(T entity) | 實體對象 | 添加單個實體 |
| updateById(T entity) | 實體對象 | 根據實體對象單個修改,對象必須至少有一個屬性和主鍵 |
| update(T entity,Wrapper wrapper) | 實體對象和條件構造器 | 根據條件修改全部,對象必須至少有一個屬性 |
| deleteById(Serializable id/T entity) | 主鍵/實體對象 | 根據主鍵刪除單個對象 |
| deleteBatchIds(Collection ids) | 主鍵集合 | 根據集合刪除 |
| delete(Wrapper wrapper) | 條件構造器 | 根據條件刪除,如果實參為null表示無條件刪除所有 |
測試
package com.hqyj.sbmp01;import com.hqyj.sbmp01.entity.Hero;
import com.hqyj.sbmp01.mapper.HeroMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;@SpringBootTest
class Sbmp01ApplicationTests {@Autowired//@Resourceprivate HeroMapper mapper;@Testvoid contextLoads() {//查詢所有List<Hero> list = mapper.selectList(null);for (Hero hero : list) {System.out.println(hero);}}@Testvoid test1() {//根據主鍵查詢單個對象System.out.println(mapper.selectById(20));}@Testvoid test2() {//根據id刪除,參數為id//System.out.println(mapper.deleteById(20));//根據id刪除,參數為帶有id的對象/*Hero hero = new Hero();hero.setId(100);System.out.println(mapper.deleteById(hero));*///刪除所有//mapper.delete(null);//根據id集合刪除ArrayList<Integer> list = new ArrayList<>();list.add(99);list.add(100);list.add(88);list.add(69);System.out.println(mapper.deleteBatchIds(list));}@Testvoid test3() {//根據id修改//參數為一個對象,這個對象至少要有一個非主鍵屬性有值Hero hero = new Hero();hero.setHeroName("a");hero.setPosition("b");hero.setSex("c");hero.setShelfDate("d");hero.setPrice(123);hero.setId(999);//System.out.println(mapper.updateById(hero));// 修改全部數據// mapper.update(修改后的對象,條件構造器)mapper.update(hero,null);}@Testvoid test4() {Hero hero = new Hero();hero.setHeroName("asdfsdfsdf");hero.setPosition("b");hero.setSex("c");hero.setShelfDate("1999-9-9");hero.setPrice(123);mapper.insert(hero);}
}
IService接口
IService接口減少業務邏輯層的代碼,并對BaseMapper進行了拓展
在業務流程類中繼承該接口
部分方法列表
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NRJfxDEr-1676548026845)(D:/qqFileRecv/框架/框架/SpringBoot+MyBatisPlus.assets/image-20230208172729669.png)]
使用
-
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) | 條件查詢單個對象 |
| save(T entity) | 添加單個對象 |
| save(Collection col) | 批量添加對象的集合 |
| updateById(T entity) | 修改,參數至少有一個屬性值和主鍵 |
| saveOrUpdate(T entity) | 添加或修改。如果實參對象的主鍵值不存在則添加,存在則修改 |
| update(T entity,Wrapper wrapper) | 條件修改,條件為null則修改全部數據 |
| removeById(Serializable id/T entity) | 根據主鍵或包含主鍵的對象刪除 |
| removeBatchByIds(Collection ids) | 根據集合刪除 |
| remove(Wrapper wrapper) | 根據條件刪除,條件為null則刪除全部 |
測試
package com.hqyj.sbmp01;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hqyj.sbmp01.entity.Hero;
import com.hqyj.sbmp01.mapper.HeroMapper;
import com.hqyj.sbmp01.service.HeroService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;@SpringBootTest
class IServiceTest {@Autowiredprivate HeroService heroService;@Testvoid test1() {//無條件查詢所有//heroService.list().forEach(System.out::println);//條件查詢,如果條件為null表示查詢所有//heroService.list(null).forEach(System.out::println);//根據主鍵查詢//Hero hero = heroService.getById(22);//System.out.println(hero);//根據條件查詢,如果條件為null表示查詢所有,會導致異常//System.out.println(heroService.getOne(null));}@Testvoid test2() {//添加save(T entity);參數對象至少要有一個屬性值//添加后,主鍵自增的值會自動賦值給主鍵屬性/*Hero hero = new Hero();hero.setHeroName("cvb");hero.setSex("男");hero.setPrice(2000);hero.setPosition("戰士");heroService.save(hero);System.out.println(hero);*///批量添加,參數為要添加的對象集合/* Hero h1 = new Hero(0, "1", "1", "1", 100, "1999-9-9");Hero h2 = new Hero(0, "2", "1", "1", 100, "1999-9-9");Hero h3 = new Hero(0, "3", "1", "1", 100, "1999-9-9");List<Hero> heroes = Arrays.asList(h1, h2, h3);heroService.saveBatch(heroes);*/Hero h1 = new Hero(0, "666", "1", "1", 100, "1999-9-9");//如果添加的對象主鍵值已存在執行修改,不存在則添加heroService.saveOrUpdate(h1);}@Testvoid test3(){//根據對象修改Hero h1 = new Hero(999, "修改后", "修改后", "女", 100, "1999-9-9");//如果對象主鍵值已存在執行修改,不存在則添加//heroService.saveOrUpdate(h1);//根據主鍵修改heroService.updateById(h1);//根據條件修改//heroService.update(null);//根據實體和條件修改,如果條件為空,會將所有數據修改為指定實體的數據//heroService.update(h1,null);}@Testvoid test4(){//根據主鍵或帶有主鍵值的對象刪除//heroService.removeById(1103302663);/*Hero hero = new Hero();hero.setId(1103302662);heroService.removeById(hero);*///批量刪除//heroService.removeBatchByIds(Arrays.asList(77,88,99));//根據條件刪除,條件為null則刪除全部heroService.remove(null);}}
條件構造器Wrapper
BaseMapper和IService接口中有很多方法都有這個參數,表示一個條件構造器對象。
如果該參數實際傳遞的值為null,表示沒有任何條件,
這個Wrapper是一個抽象類,如果想要帶條件,就要創建一個該類的子類對象。
常用子類為QueryWrapper和UpdateWrapper,
查詢是創建QueryWrapper對象,更新時創建UpdateWrapper,實際使用無區別。
Wrapper對象帶參數
Wrapper<T> wrapper = new QueryWrapper(T entity);
QueryWrapper<T> wrapper = new QueryWrapper(T entity);
Wrapper構造方法的參數如果是一個實體對象,只要該對象的屬性不為空,就會將所有屬性用and拼接起來作為條件。
這種適合已知某個字段為某個值時使用。
@Test
void test1() {//創建實體對象,只帶兩個屬性值Hero hero = new Hero();hero.setHeroName("瑞茲");hero.setSex("女");//創建一個帶實體參數的條件構造器對象Wrapper<Hero> wrapper = new QueryWrapper<>(hero);System.out.println(heroService.getOne(wrapper));
}
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-yizbWK9T-1676548026845)(D:/qqFileRecv/框架/框架/day14-SpringBoot+MyBatisPlus2.assets/image-20230209095105726.png)]
Wrapper對象不帶參數
Wrapper<T> wrapper = new QueryWrapper();
QueryWrapper<T> wrapper = new QueryWrapper();
當條件構造器不帶參數時,就需要自定義條件。
這種適用于自定義查詢條件。
默認多個條件時,用and連接,如果條件之間要使用or,調用or()方法
指定或范圍
@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,Object val2) between and column between val1 and bal2* notBetween(String column Object val1,Object val2) not between and column not between val1 and bal2* *///創建一個不帶實體參數的條件構造器對象QueryWrapper<Hero> wrapper = new QueryWrapper<>();//自定義條件:指定值//查詢性別為男的數據 eq equals =//wrapper.eq("sex","男");//查詢性別不為男的數據 ne not equals <>//wrapper.ne("sex","男");//wrapper.between("price",1000,4000);//查詢價格小于5000的戰士//多個條件默認用and拼接/*wrapper.lt("price",5000);wrapper.eq("position","戰士");*///查詢性別為女或價格大于3000//or()方法會將前后的條件使用or拼接/*wrapper.gt("price",3000);wrapper.or();wrapper.eq("sex","女");*///查詢性別為男的戰士或價格大于3000的法師//支持鏈式寫法wrapper.eq("sex","男").eq("position","戰士").or().eq("position","法師").gt("price","3000");heroService.list(wrapper).forEach(System.out::println);
}
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-c1gIsBNh-1676548026846)(D:/qqFileRecv/框架/框架/day14-SpringBoot+MyBatisPlus2.assets/image-20230209102606876.png)]
空值和模糊查詢
@Test
void test3() {/** isNull(String column) column is null* isNotNull(String column) column is not null* like(String column,Object val) column like '%val%'* likeLeft(String column,Object val) column like '%val'* likeRight(String column,Object val) column like 'val%'* notLike(String column,Object val) column not like '%val%'* notLikeLeft(String column,Object val) column not like '%val'* notLikeRight(String column,Object val) 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","琳").isNull("shelf_date");heroService.list(wrapper).forEach(System.out::println);
}
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FUvSAhMg-1676548026846)(D:/qqFileRecv/框架/框架/day14-SpringBoot+MyBatisPlus2.assets/image-20230209104130810.png)]
集合和排序
@Testvoid test4() {/** 集合* in(String column,Object... vals) column in (val1,val2...)* in(String column,Collection vals) column in (val1,val2...)* notIn(String column,Object... 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","法師");//可變參數//wrapper.in("position","戰士","法師");//集合//wrapper.in("position", Arrays.asList("戰士","法師"));//所有男性角色,按價格降序wrapper.eq("sex","男").orderByDesc("price");heroService.list(wrapper).forEach(System.out::println);}
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-lkh0EDLG-1676548026847)(D:/qqFileRecv/框架/框架/day14-SpringBoot+MyBatisPlus2.assets/image-20230209105104809.png)]
分組和聚合函數(自定義查詢)
分組通常會和聚合函數(count、sum、min、max、avg)一起使用,但MyBatisPlus中沒有現成的聚合函數。
需要使用**select(String… fields)**自定義查詢字段。
如查詢每個每個位置的總數
條件構造器對象.select(“position”,“count(id)”),對應的sql查詢部分為select “position”,“count(id)” from hero
@Test
void test() {/** 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)");//查詢不同性別的人數//select sex,count(id) from hero group by sex//wrapper.select("sex","count(id)");//根據指定字段分組//wrapper.groupBy("sex");//當前的sql語句查詢的結果是一個訂制數據,如查詢總數只會得到一個數據,所以使用listMaps()方法//List<Map<String, Object>> maps = heroService.listMaps(wrapper);//根據定位分組,查詢值為法師、戰士的人數,按人數降序wrapper.select("position","count(id) as count").in("position","戰士","法師").groupBy("position").orderByDesc("count");List<Map<String, Object>> maps = heroService.listMaps(wrapper);System.out.println(maps);
}
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-H3xj8aEw-1676548026847)(D:/qqFileRecv/框架/框架/day14-SpringBoot+MyBatisPlus2.assets/image-20230209110021881.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ecySX50A-1676548026847)(D:/qqFileRecv/框架/框架/day14-SpringBoot+MyBatisPlus2.assets/image-20230209112235219.png)]
分頁查詢
MyBatisPlus中集成了分頁功能,只需在項目的啟動類中注入一個分頁攔截器對象
/*注入一個MyBatisPlus提供的分頁攔截器對象定義一個方法,在方法上加入@Bean注解,將該方法的返回值注入到Spring容器中*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){//創建一個MybatisPlus攔截器對象MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//通過攔截器對象添加分頁攔截器對象,設置數據庫類型interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;
}
@Test
void test1(){//無條件分頁查詢page(Page pageInfo)//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());//條件分頁page(Page pageInfo,Wrapper wrapper)//分頁查詢所有法師pageInfo= heroService.page(pageInfo,new QueryWrapper<Hero>().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", "root").globalConfig(builder -> {builder.author("HQYJ") // 設置作者,生成文件的注釋里的作者.outputDir("F:\\框架\\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();}
}
運行該類即可自動生成
MyBatisPlus中的邏輯刪除和物理刪除
物理刪除:將數據真正刪除。delete from 表 where id=值
邏輯刪除:數據不刪除,而是打個"被刪除"的標記。額外添加一個是否被刪除的字段"deleted",其值為0或1,0表示未刪除,1表示已刪除。
? 當刪除某條數據時,執行修改 update 表 set deleted=1 where id=值
添加了"deleted"字段后,查詢要更改為select * from 表 where deleted=0
實現過程
1.在表中添加一個新字段
deleted:默認為0表示未刪除,1表示已刪除
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QOCHl702-1676548026848)(D:/qqFileRecv/框架/框架/day14-SpringBoot+MyBatisPlus2.assets/image-20230209171301480.png)]
2.在實體類中對應屬性,添加@TableLogic注解
@TableLogic//該注解表示該字段是邏輯刪除字段
private Integer deleted;
此時調用IService接口中的list()方法查詢所有時,會在條件中加入deleted=0
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ejE8Xe5w-1676548026848)(D:/qqFileRecv/框架/框架/day14-SpringBoot+MyBatisPlus2.assets/image-20230209171655736.png)]
此時調用IService接口中的刪除時,會執行修改
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-8NijaP96-1676548026848)(D:/qqFileRecv/框架/框架/day14-SpringBoot+MyBatisPlus2.assets/image-20230209171854655.png)]
Thymeleaf
SpringBoot官方建議使用的頁面模板引擎,代替之前的JSP。
它是以HTML為基礎,可以展示靜態數據,方便前端人員開發靜態頁面,
也可以通過Thymeleaf的語法,配合EL輸出由控制器跳轉而來的動態數據。
Thymeleaf從入門到精通 - 知乎 (zhihu.com)
用法
1.添加依賴
<!--thymeleaf頁面模板引擎-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId><version>2.7.3</version>
</dependency>
2.在temlates目錄下創建一個HTML頁面
在頁面的HTML標簽中,加入xmlns:th="http://www.thymeleaf.org"屬性。
可以通過修改IDEA中的HTML頁面模板,之后每次創建HTML頁面都會有該屬性
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gtk3shs1-1676548026849)(D:/qqFileRecv/框架/框架/day14-SpringBoot+MyBatisPlus2.assets/image-20230209143658152.png)]
3.thymeleaf具體使用
-
雙標簽中的輸出變量: th:text
<h1 th:text="${作用域中某個對象名}"></h1><h1 th:text="${username}"></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><table><tr th:each="hero:${heroList}"><td th:text="${hero.id}"></td><td th:text="${hero.name}"></td><td th:text="${hero.sex}"></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><a th:href="@{/question/findById(id=${question.quesCode})}">編輯</a>
-
-
表單提交路徑:th:action=“@{/}”
<form methos="post" th:action="@{/hero/insert}"></form> -
判斷:th:if
<h1 th:if="判斷邏輯">用戶信息</h1><h1 th:if="${userinfo.username==null}">請登錄</h1> -
選中單選按鈕或復選框:th:checked
<input type="radio" value="男" name="sex" th:checked="邏輯判斷"><input type="radio" value="男" name="sex" th:checked="${user.sex=='男'}">男 <input type="radio" value="女" name="sex" th:checked="${user.sex eq '女'}">女 -
選中下拉菜單:th:selected
<select><option th:selected="邏輯判斷"></option> </select><select><option th:selected="${book.typeId==bookType.typeId}" th:each="bookType:${list}" th:value="${bookType.typeId}" th:text="${bookType.typeName}"></option> </select> -
src屬性:th:src
<script th:src="@{/bootstrap-3.4.1-dist/js/jquery-3.6.2.min.js}"></script> -
內聯表達式
Thymeleaf頁面中使用[[]]可以在script標簽中使用EL表達式,這兩對中括號拼接的內容稱為內聯表達式。
在給script標簽加入**th:inline=“javascript”**后使用
<script th:inline="javascript">$(function () {//遍歷頁碼$(".pno").each(function (){//如果頁碼和當前頁一致,高亮顯示if($(this).text()==[[${pageInfo.current}]]){$(this).addClass("active");}});}); </script><script th:inline="javascript">$(function () {//使用ajax讀取所有的題目類型對象,遍歷成option$.ajax({url:[[@{/qc/queryAll}]],success:function (res){for(var i=0;i<res.length;i++){let qcId = res[i].qcId;let qcName = res[i].qcName;$("<option></option>").text(qcName).val(qcId).appendTo($("select[name=qcId]"));}}});}); </script>單表條件分頁
原理:調用IService接口中的**page(Page page,Wrapper wrapper)**方法,創建分頁模型對象和條件構造器對象
在Springboot啟動類中加入mybatis分頁攔截器
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor; }搜索框
<form class="navbar-form navbar-left" th:action="@{/question/queryAll}"><div class="form-group"><input type="text" class="form-control" name="keyword" placeholder="請輸入題目關鍵字"></div><button type="submit" class="btn btn-default">搜索</button> </form>控制層
@RequestMapping("/queryAll") public String queryAll(@RequestParam(defaultValue = "1") Integer page,@RequestParam(defaultValue = "5") Integer size,@RequestParam(defaultValue = "") String keyword,Model model) {//1.創建分頁模型對象Page<Question> pageInfo = new Page<>(page, size);//創建條件構造器對象QueryWrapper<Question> wrapper = new QueryWrapper<>();wrapper.like("ques_title",keyword);//2.調用條件分頁查詢的方法questionService.page(pageInfo,wrapper);// 將分頁模型對象保存到請求中model.addAttribute("pageInfo", pageInfo);//構造頁數List<Long> pageList = new ArrayList<>();//每次最多顯示5頁//定義最大數字long maxPage = page + 4 > pageInfo.getPages() ? pageInfo.getPages() : page + 4;//定義最小數字long minPage = page - 4 < 1 ? page : maxPage - 4;for (long i = minPage; i <= maxPage; i++) {pageList.add(i);}model.addAttribute("pageList", pageList);return "questionList"; }分頁組件
<nav aria-label="Page navigation"><ul class="pagination"><li><a th:if="${pageInfo.hasPrevious}" th:href="@{/question/queryAll(keyword=${param.keyword},page=${pageInfo.current-1})}"aria-label="Previous"><span aria-hidden="true">«</span></a></li><li class="pno" th:each="pno:${pageList}"><a th:href="@{/question/queryAll(keyword=${param.keyword},page=${pno})}" th:text="${pno}"></a></li><li><a th:if="${pageInfo.hasNext}" th:href="@{/question/queryAll(keyword=${param.keyword},page=${pageInfo.current+1})}"aria-label="Next"><span aria-hidden="true">»</span></a></li></ul> </nav>MyBatisPlus關聯查詢
可以通過創建xml文件來實現,過程同MyBatis關聯查詢。
這里使用注解實現。
主表question_catalog
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Nc9I9gWS-1676548026849)(D:/qqFileRecv/框架/框架/day15-SpringBoot+MyBatisPlus3.assets/image-20230210142342982.png)]
從表question
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-IshUMlt8-1676548026849)(D:/qqFileRecv/框架/框架/day15-SpringBoot+MyBatisPlus3.assets/image-20230210142322936.png)]
多對一
查詢時以從表數據為主體,關聯對應的主表信息。
多個question對象對應一個question_catalog對象。
在查詢question表的同時,顯示關聯question_catalog表中的數據。
實體類
在從表的實體類中,額外添加一個外鍵對應的主表實體對象
@Data public class Question implements Serializable {private static final long serialVersionUID = 1L;@TableId(type = IdType.ASSIGN_UUID)private String quesCode;private Integer qcId;private String quesTitle;private String quesOptA;private String quesOptB;private String quesOptC;private String quesAns;@TableLogic//該注解表示該字段是邏輯刪除字段private Integer deleted;/** 如果在實體類中添加了一個不屬于該表中字段的屬性* 要在該屬性上加@TableField(exist = false)表示該屬性沒有對應的字段* */@TableField(exist = false)private QuestionCatalog qc; }原理
如果直接通過IService接口中的list()方法查詢,實際調用的是BaseMapper接口中的selectList()方法,默認查詢自身表。
如果重寫業務層中的list()方法,或在業務層中自定義一個方法,讓其調用數據訪問層中自定義的某個方法,重新定制sql語句,就能得到想要的數據
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-zc2Egb23-1676548026850)(D:/qqFileRecv/框架/框架/day15-SpringBoot+MyBatisPlus3.assets/image-20230210145612843.png)]
實現過程
1.在Mapper(QuestionMapper)接口中自定義一個方法,使用注解編寫sql語句
有兩種方式:
-
方式一:構造自定義的sql語句
- 建議外鍵關聯字段比較少的情況下使用
- sql語句需要連接查詢,將外鍵關聯的表的字段重命名為"外鍵實體對象名.屬性名"
@Select("select q.*,qc_name as 'qc.qcName' from question q,question_catalog qc where q.qc_id=qc.qc_id ") List<Question> mySelect(); -
方式二:使用子查詢,自定義結果集映射
- 建議外鍵關聯字段比較多的情況下使用
- 先查詢從表,再使用外鍵字段查詢主表
- 外鍵字段需要重新映射一次
//1.查詢自身表 @Select("select * from question") //2.自定義結果集映射 @Results({//qc_id字段在下面的子查詢中已經使用了,將外鍵字段qc_id重新映射@Result(property = "qcId",column = "qc_id"),//遇到qc屬性,使用qc_id進行子查詢//執行QuestionCatalogMapper中的selectById方法//該方法是BaseMapper接口中默認就存在的方法@Result(property ="qc" ,column ="qc_id",one =@One(select = "com.hqyj.question_sys.mapper.QuestionCatalogMapper.selectById")) }) List<Question> mySelect();
2.在Service層中調用Mapper層中自定義的方法
@Service public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements IQuestionService {/** 如果直接調用該方法查詢,會最終調用BaseMapper中的selectList()方法* 這里重寫該方法,讓其調用BaseMapper中的自定義方法* */@Overridepublic List<Question> list() {return baseMapper.mySelect();} }一對多
以主表為主體,查詢時顯示關聯的從表對象集合。
查詢question_catalog表,同時顯示qc_id對應的question表中的數據集合。
實體類
在主表實體類中,額外添加一個從表實體對象集合。
@TableName("question_catalog") @Data public class QuestionCatalog implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "qc_id", type = IdType.AUTO)private Integer qcId;private String qcName;/** 一對多查詢,定義關聯的從表對象集合* @TableField(exist = false)標明該屬性沒有對應的字段* */@TableField(exist = false)private List<Question> questionList; }原理同多對一查詢,重寫service層中的list()方法,改變原本方法中調用的內容
實現過程
1.在Mapper(QuestionCatalogMapper)接口中自定義一個方法,使用注解編寫sql語句
/** 自定義查詢* 使用子查詢實現一對多* */ //1.查詢question_catalog表 @Select("select * from question_catalog") //2.自定義結果集映射 @Results({//由于在下面使用qc_id進行了子查詢,對其重新映射@Result(property = "qcId",column = "qc_id"),//3.使用qc_id進行子查詢,查詢question表,調用根據qc_id查詢對應數據集合的方法,沒有現成的方法,需要自定義@Result(property = "questionList", column = "qc_id", many = @Many(select = "com.hqyj.question_sys.mapper.QuestionMapper.queryByQcId")) }) List<QuestionCatalog> mySelect();2.在Mapper(QuestionMapper)接口中定義方法
/** 根據qc_id查詢對應數據集合* */ @Select("select * from question where qc_id=#{qcId}") List<Question> queryByQcId();3.在Service層中調用Mapper層中自定義的方法
/** 重寫原本的查詢所有,執行自定義的查詢* */ @Override public List<QuestionCatalog> list() {return baseMapper.mySelect(); }多對一關聯查詢條件分頁
原理
參考原本的條件分頁查詢方法
selectPage(P page, Wrapper<Question> queryWrapper)page用于分頁,queryWrapper用于構造條件。通過自定義的sql語句關聯兩張表實現。
在自定義的sql語句中,通過ew.sqlSegment或{ew.sqlSegment}或ew.sqlSegment或{ew.customSqlSegment}使用條件構造器Wrapper對象
${ew.sqlSegment} 將條件原樣拼接
${ew.customSqlSegment} 在所有條件前自動加入where關鍵字
實現過程
1.在Mapper(QuestionMapper)中自定義關聯查詢的sql
- ${ew.sqlSegment} 將條件原樣拼接
- ${ew.customSqlSegment} 在所有條件前加入where關鍵字
//${ew.sqlSegment} 將條件原樣拼接 //@Select("SELECT q.*,qc_name AS 'qc.qcName' FROM question q,question_catalog qc WHERE q.qc_id = qc.qc_id and ${ew.sqlSegment}")//${ew.customSqlSegment} 在所有條件前加入where關鍵字 @Select("SELECT q.*,qc_name AS 'qc.qcName' FROM question q inner join question_catalog qc on q.qc_id = qc.qc_id ${ew.customSqlSegment}") <P extends IPage<Question>> P queryByCondition(P page, @Param("ew") Wrapper<Question> queryWrapper);2.在service層中重寫或自定義方法,調用mapper層中自定義的方法
@Override public <E extends IPage<Question>> E page(E page, Wrapper<Question> queryWrapper) {return baseMapper.queryByCondition(page,queryWrapper); }3.controller層
- 接收所需參數
- 創建分頁模型對象
- 創建條件構造器對象
@RequestMapping("/queryAll") public String queryAll(@RequestParam(defaultValue = "1") Integer page,@RequestParam(defaultValue = "5") Integer size,@RequestParam(defaultValue = "") String keyword,@RequestParam(defaultValue = "0") Integer qcId,Model model) {//1.創建分頁模型對象Page<Question> pageInfo = new Page<>(page, size);//創建條件構造器對象QueryWrapper<Question> wrapper = new QueryWrapper<>();wrapper.like("ques_title", keyword);if (qcId != 0) {wrapper.eq("q.qc_id", qcId);}//2.調用條件分頁查詢的方法questionService.page(pageInfo, wrapper);model.addAttribute("pageInfo", pageInfo);//構造頁數List<Long> pageList = new ArrayList<>();//每次最多顯示5頁//定義最大數字long maxPage = page + 4 > pageInfo.getPages() ? pageInfo.getPages() : page + 4;//定義最小數字long minPage = page - 4 < 1 ? page : maxPage - 4;for (long i = minPage; i <= maxPage; i++) {pageList.add(i);}model.addAttribute("pageList", pageList);return "questionList"; }4.頁面表單
<form class="navbar-form navbar-left" th:action="@{/question/queryAll}"><div class="form-group"><input type="text" class="form-control" name="keyword" placeholder="請輸入題目關鍵字"><select class="form-control" name="qcId"></select></div><button type="submit" class="btn btn-default">搜索</button> </form> <script th:inline="javascript">$(function () {$.ajax({url:[[@{/qc/queryAll}]],success:function (res){for(var i=0;i<res.length;i++){let qcId = res[i].qcId;let qcName = res[i].qcName;$("<option></option>").text(qcName).val(qcId).appendTo($("select[name=qcId]"));}}});}); </script> -
答題功能核心內容
隨機出題
/** 隨機5條記錄* */
@Select("select * from question order by rand() limit 5 ")
List<Question> queryByRand();
答題頁面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"><head><title>Title</title><link rel="shortcut icon" href="https://www.baidu.com/favicon.ico" type="image/x-icon"><!--bootstrap的css文件路徑--><link th:href="@{/bootstrap-3.4.1-dist/css/bootstrap.css}" rel="stylesheet"></head><body><div class="col-md-4"></div><div class="col-md-4"><form id="result"><div class="panel panel-primary" th:each="question:${list}"><div class="panel-heading" th:text="${question.quesTitle}"></div><div class="panel-body"><div class="form-group"><label><input type="radio" value="a" th:name="${question.quesCode}"><span th:text="${question.quesOptA}"></span></label><label><input type="radio" value="b" th:name="${question.quesCode}"><span th:text="${question.quesOptB}"></span></label><label><input type="radio" value="c" th:name="${question.quesCode}"><span th:text="${question.quesOptC}"></span></label></div></div></div><button class="btn btn-default loginBtn" type="submit">提交</button></form></div><div class="col-md-4"></div><script th:src="@{/bootstrap-3.4.1-dist/js/jquery-3.6.2.min.js}"></script><script th:inline="javascript">$(function () {$("#result").submit(function () {//創建數組保存答題結果var ansList = [];//獲取表單的數據 表單對象.serialize()可以獲取表單中每個name-value,將其拼接為字符串var data = $(this).serialize();//name=value&name=value//切分答題結果let list = data.split("&");//遍歷答題結果,將其構造為一個答題結果對象for (var i = 0; i < list.length; i++) {//創建一個對象var ans = {};//構造對象的屬性ans.code = list[i].split("=")[0];ans.opt = list[i].split("=")[1];ansList.push(ans);}//提交ansList$.ajax({url: [[@{/question/result}]],data: {ansList: JSON.stringify(ansList)},traditional: true,success: function (res) {alert("你答對了"+res+"道題");}});return false;})});</script></body>
</html>
答題
答題對象
package com.hqyj.question_sys.entity;import lombok.Data;/*
* 創建答題結果對象
* */
@Data
public class AnswerResult {private String code;private String opt;
}
controller
@RequestMapping("/result")
@ResponseBody
public Integer result(String ansList) throws JsonProcessingException {//將JSON格式的字符串轉換為對象//SpringBoot中集成了jackson,可以用于對象和JSON之間的轉換//創建ObjectMapper對象ObjectMapper jsonTool = new ObjectMapper();//讀取接收到的JSON字符串,轉換為Result對象數組AnswerResult[] list = jsonTool.readValue(ansList, AnswerResult[].class);//遍歷答題結果Integer count=0;for (AnswerResult res : list) {/*//答題題目String code = res.getCode();//回答結果String opt = res.getOpt();//原本題目Question question = questionService.getById(code);//真正答案String quesAns = question.getQuesAns();*/if (questionService.getById(res.getCode()).getQuesAns().equals(res.getOpt())) {count++;}}return count;
}
Spring Data JPA
2001年推出了Hibernate,是一個全自動ORM框架。可以不用編寫SQL語句,就能實現對數據庫的持久化操作。
SUN公司在Hibernate的基礎上,制定了JPA,全稱 Java Persisitence API,中文名Java持久化API,
是一套Java訪問數據庫的規范,由一系列接口和抽象類構成。
后來Spring團隊在SUN公司制定的JPA這套規范下,推出了Spring Data JPA,是JPA的具體實現。
如今常說的JPA,通常指Spring Data JPA。
SpringBoot集成Spring Data JPA
1.創建SpringBoot項目,選擇依賴
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-VHccTb66-1676548026850)(D:/qqFileRecv/框架/框架/day16-SpringBoot+JPA.assets/image-20230213141450488.png)]
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=root# 設置數據庫類型
spring.jpa.database=mysql
# 打印SQL語句
spring.jpa.show-sql=true
3.創建實體類
-
類上加**@Entity**注解
-
主鍵屬性上加
-
@Id注解標明主鍵
-
**@GeneratedValue(strategy = GenerationType.IDENTITY)**設置MySQL數據庫主鍵生成策略,數據庫設置為自增
-
-
其他屬性名與字段名一致或駝峰命名法
- 如果字段名多個單詞之間用_,使用駝峰命名法
- 如果不相同,使用**@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_price")private Integer price;private Integer bookNum;private String publisher_date;
}
4.數據訪問層接口
- 類上加@Repository注解
- 繼承JpaRepository<實體類型,主鍵類型>接口
@Repository
public interface BookInfoDao extends JpaRepository<BookInfo,Integer> {}
5.測試常用方法
| 方法名 | 返回值 | 作用 |
|---|---|---|
| findAll() | List | 查詢所有數據。 |
| save(T entity) | T entity | 添加或修改。如果不存在主鍵屬性或主鍵值不存在,執行添加;如果存在主鍵屬性且有該主鍵值,執行修改。 |
| delete(T entity) | void | 根據對象刪除。如果該對象中有主鍵屬性且有該主鍵值,根據該主鍵值刪除。 |
| findById(主鍵) | Optional | 根據主鍵值查詢。返回的對象調用isPresent()結果為true,表示查詢到了數據,繼續調用get()得到查詢到的對象。 |
@SpringBootTest
class Day16SpringBootJpaApplicationTests {@Autowiredprivate BookInfoDao bookInfoDao;@Testvoid contextLoads() {List<BookInfo> all = bookInfoDao.findAll();all.forEach(System.out::println);}@Testvoid insert() {BookInfo bookInfo = new BookInfo();bookInfo.setBookName("測試");bookInfo.setBookAuthor("測試");bookInfo.setBookNum(156);bookInfo.setPrice(20);bookInfo.setTypeId(1);//save()方法調用時,如果對象中沒有主鍵或主鍵值不存在,作為添加使用BookInfo save = bookInfoDao.save(bookInfo);//添加成功后,會自動獲取主鍵自增的值System.out.println(save);}@Testvoid update() {BookInfo bookInfo = new BookInfo();bookInfo.setBookName("xxxxxxxxxxx");bookInfo.setBookAuthor("測試");bookInfo.setBookNum(356);bookInfo.setPrice(23);bookInfo.setTypeId(1);//save()方法調用時,如果對象中有主鍵且存在,作為修改使用BookInfo save = bookInfoDao.save(bookInfo);//修改成功后,返回修改后的對象System.out.println(save);}@Testvoid delete() {//根據主鍵值刪除,如果值不存在,會報錯//bookInfoDao.deleteById(36);//根據對象刪除,如果對象中包含主鍵值則刪除,如果沒有值或不存在,不會報錯BookInfo bookInfo = new BookInfo();//bookInfo.setBookId(330);bookInfoDao.delete(bookInfo);}@Testvoid findOne() {//根據主鍵查詢,返回值Optional類型Optional<BookInfo> byId = bookInfoDao.findById(60);//isPresent()如果為true表示查詢到了數據if (byId.isPresent()) {//get()將查詢到的數據轉換為對應的實體類BookInfo bookInfo=byId.get();System.out.println(bookInfo);}else{System.out.println("未查詢到數據");}}
}
JPA進階
分頁查詢
調用數據訪問層中的**findAll(Pageable pageable)**方法,即可實現分頁。
參數Pageable是org.springframework.data.domain包中的一個接口,通過其實現類
PageRequest,調用靜態方法of(int page,int size),當做Pageable對象使用。
這里的page從0開始為第一頁。
@Test
void queryByPage(){//PageRequest是Pageable的實現類,調用靜態方法of(int page,int size)//這里的page的值0表示第一頁//調用findAll(Pageable pageable)方法,返回分頁模型對象Page<BookInfo> pageInfo = bookInfoDao.findAll(PageRequest.of(0,5));//分頁相關數據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中定義了queryById(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 = name |
| Or/And | findByBookNameOrBookAuthor(String name,String author) | book_name = name or book_author = author |
| After/Befor | findByBookPriceAfter(double price) | book_price > price |
| GreaterThanEqual/LessThanEqual | findByBookNumLessThanEqual(int num) | book_num <= num |
| Between | findByBookNumBetween(int min,int max) | book_num between min and max |
| Is[Not]Null | findByPublisherDateIsNull() | publish_date is null |
| [Not]Like | findByBookNameLike(String condition) | book_name like ‘condition’ |
| [Not]Contains | findByBookNameContains(String keyword) | book_name like ‘%keyword%’ |
| StartsWith/EndsWith | findByBookNameStartsWith(String firstName) | book_name like ‘firstName%’ |
| 無條件排序:findAllByOrderBy字段[Desc/Asc] | findAllByOrderByBookId() | order by book_id asc |
| 有條件排序:findAllBy條件OrderBy字段[Desc/Asc] | findAllByTypeIdOrderByBookIdDesc() | type_id = ? order by book_id desc |
@Repository
public interface BookInfoDao extends JpaRepository<BookInfo,Integer> {//指定值查詢//根據書名查詢List<BookInfo> getAllByBookName(String x);//查詢價格大于指定值 字段對應的屬性名 After/GreaterThanList<BookInfo> findAllByPriceAfter(int price);//查詢價格小于于指定值 字段對應的屬性名 Before/LessThanList<BookInfo> findAllByPriceLessThan(int price);//查詢庫存大于等于指定值 GreaterThanEqualList<BookInfo> queryAllByBookNumGreaterThanEqual(int num);//查詢庫存在指定閉區間內 Between(int min,int max)List<BookInfo> findAllByBookNumBetween(int min,int max);//空值查詢 null//查詢出版日期為空 IsNull/IsNotNullList<BookInfo> findAllByPublisherDateIsNull();//書名中帶有關鍵字 Like/NotLike 實參一定要使用%或_List<BookInfo> getAllByBookNameLike(String keyword);//作者名中帶有關鍵字 Contains/NotContains 實參只需要關鍵字List<BookInfo> getAllByBookAuthorContains(String keyword);//指定作者的姓 指定開頭/結尾 StartsWith/EndsWithList<BookInfo> getAllByBookAuthorStartsWith(String keyword);//查詢所有數據,按價格降序 無條件排序 OrderBy字段[Desc/Asc]List<BookInfo> getAllByOrderByPriceDesc();//查詢指定類型,按id降序List<BookInfo> getAllByTypeIdOrderByBookIdDesc(Integer typeId);}
條件分頁查詢
只需在定義方法時,將方法的返回值設置為Page類型,在參數中就如Pageable對象
//根據作者和書名的關鍵字分頁查詢
Page<BookInfo> getAllByBookNameContainsOrBookAuthorContains(String nameKeyword,String authorKeyword,Pageable pageable);
Page<BookInfo> pageInfo = bookInfoDao.getAllByBookNameContainsOrBookAuthorContains("龍","山",PageRequest.of(0,5));
//分頁相關數據保存在pageInfo對象中
聚合函數分組查詢
自定義SQL
在數據訪問層接口中的方法上,可以加入@Query注解,默認要使用HQL(Hibernate專用)格式的語句。
如果要使用原生的SQL語句,需要添加nativeQuery=true屬性,用value屬性定義SQL語句
/** 在JPA中,如果要使用自定義的SQL語句* nativeQuery = true 開啟原生SQL語句* value="sql語句"* */
@Query(nativeQuery = true, value = "select book_author,count(book_id) from book_info group by book_author")
List testQuery();
@Test
void test(){List list = bookInfoDao.testQuery();//查詢的結果為集合,集合中保存的是每一行數據for (Object row : list) {//每一行頁數一個對象數組Object[] obj= (Object[])row;//根據索引得到查詢出的內容System.out.println(obj[0]+"---"+obj[1]);}
}
自定義SQL中帶參數
SQL語句中的":XXX"表示參數
如果方法的形參名和xxx一致時直接使用,如果不一致,在形參上加入@Param注解設置形參名
/** 根據作者查詢其圖書總庫存* 使用":形參名"在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 test(){List list = bookInfoDao.testQuery3("金庸");//查詢的結果為集合,集合中保存的是每一行數據for (Object row : list) {//每一行頁數一個對象數組Object[] obj= (Object[])row;//根據索引得到查詢出的內容System.out.println(obj[0]+"---"+obj[1]);}
}
關聯查詢
主表book_type
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ZCwFQTbl-1676548026851)(D:/qqFileRecv/框架/框架/day16day17-SpringBoot+JPA.assets/image-20230214105234875.png)]
從表book_info
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-sM1NxWG1-1676548026851)(D:/qqFileRecv/框架/框架/day16day17-SpringBoot+JPA.assets/image-20230214105247332.png)]
實體類
主表實體BookType
@Entity
@Data
public class BookType {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer typeId;private String typeName;
}
從表實體BookInfo
- 無需寫出外鍵字段屬性
- 額外添加外鍵字段對應的實體類對象屬性
@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_price")private Integer price;private Integer bookNum;private String publisherDate;//多對一查詢,以當前從表信息為主體,關聯相應的主表信息@JoinColumn(name = "type_id")//使用type_id字段進行多對一查詢@ManyToOne//多對一private BookType bt;
}
使用
-
多對一查詢,調用dao中的查詢方法,就會自動給外鍵字段對應的對象賦值
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-e6GbVH67-1676548026851)(D:/qqFileRecv/框架/框架/day16day17-SpringBoot+JPA.assets/image-20230214111732161.png)]
-
添加時,參數所需外鍵字段,使用外鍵字段對應的對象名代替
//添加 @PostMapping("/book") public RestResult<BookInfo> insert(BookInfo bookInfo) {return RestResult.ok("添加成功", bookInfoDao.save(bookInfo)); }[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-diRNCC1k-1676548026852)(D:/qqFileRecv/框架/框架/day16day17-SpringBoot+JPA.assets/image-20230214113125279.png)]
-
如果根據外鍵字段查詢,要在從表dao中定義方法findBy外鍵對象名_外鍵對象屬性名(數據類型 外鍵字段)
//如果根據外鍵字段查詢,方法名寫為 By外鍵實體類屬性名_屬性名 List<BookInfo> getAllByBt_TypeId(Integer typeId);
前后端分離項目
前后端分離,就是將web應用中的前端頁面和后端代碼分開完成、部署。
- 前后端的開發者只需要完成各自的事情,最終以文檔的形式約定數據接口(URL、參數、返回值、請求方式)
- 前后端分別用獨立的服務器
- 后端只需處理數據并提供訪問接口(路徑),以RESTFul風格的JSON格式傳輸數據
- 前端只需負責渲染頁面和展示數據
傳統項目和前后端分離項目對比
傳統項目
前端和后端的代碼運行在一個服務器上,頁面經由控制器跳轉
SSM項目、圖書管理系統、答題系統
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fthntOJP-1676548026852)(D:/qqFileRecv/框架/框架/day16-SpringBoot+JPA.assets/image-20230213170658853.png)]
前后端分離項目
前后端的代碼分別運行在各自的服務器上
后端提供JSON格式字符串的數據接口
前端負責跳轉、解析JSON數據。
酒店客房管理系統
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-n5XClYA1-1676548026852)(D:/qqFileRecv/框架/框架/day16-SpringBoot+JPA.assets/image-20230213171038191.png)]
前后端分離項目后端控制層設計
請求方式設計:RESTFul風格
風格,不是標準,可以不用強制遵循。
RESTFul風格:用不同的請求方式去訪問同一個URL地址時,執行不同的操作。
特點
- 通過URL就能知道當前在哪個模塊
- 通過不同的請求方式決定執行什么操作
- 通過返回的狀態碼得到操作結果
使用RESTFul風格和普通方式對比
普通方式
localhost:8080/user/queryAll 查詢所有
localhost:8080/user/queryById?id=1001 條件查詢
localhost:8080/user/insert?name=ez&sex=男&age=20 添加
localhost:8080/user/update?name=ez&id=1001 修改
localhost:8080/user/delete?id=1001 刪除
RESTFul風格
localhost:8080/user 查詢所有get請求
localhost:8080/user/1001 條件查詢get請求
localhost:8080/user 添加post請求
localhost:8080/user 修改put請求
localhost:8080/user/1001 刪除delete請求
RESTFul風格具體使用
-
在請求映射的命名上,統一用小寫字母的名詞形式表示當前位于哪個模塊。如/user、/book_info
-
訪問時如果要傳參,使用"/模塊名/參數"方式,配合controller中的@PathVariable獲取
@GetMapping("/book/{id}") public BookInfo queryById(@PathVariable("id")Integer id){return service.findById(id); } -
在controller的方法上,使用@XXXMapping()設置訪問該方法的請求方式
- @GetMapping(“路徑”) 查詢
- @PostMapping(“路徑”) 添加
- **@PutMapping(“路徑”) ** 修改
- **@DeleteMapping(“路徑”) ** 刪除
- @RequestMapping(value=“路徑”,method=RequestMethod.GET/POST/PUT/DELETE))
-
如果請求方式不匹配,會報405異常
-
在同一個controller中,不能出現兩個請求方式和路徑都一致的方法
返回值設計
前后端分離項目的控制層方法的返回值也需要進行統一。
返回值通常包含以下信息
- 傳遞狀態,用狀態碼表示Integer code
- 傳遞消息,用字符串表示String msg
- 傳遞集合,用集合表示List list
- 傳遞對象,用對象表示Object obj
將這些信息封裝到一個對象中,這個對象稱為返回結果類RestResult對象
RestResult類具體設計
Postman
Postman API Platform | Sign Up for Free
Java技術棧
服務器端
? JavaSE(API、OOP)、JavaEE(Servlet、Spring、SpringMVC)
? tomcat
前端0
? HTML+CSS+JS+JQUERY、Bootstrap、LayUI、EasyUI、VUE
數據庫
? MySQL、Oracle、Redis
? JDBC、SpringJDBC、MyBatis、MyBatisPlus、JPA
工具
? IDEA、HBuilder、VSCode、Notepad++、Sublime、Navicat、Postman
JavaEE過程
Servlet + JDBC + JSP(bootstrap) 圖書商場
Spring + SpringMVC + SpringJDBC + JSP(bootstrap)
Spring + SpringMVC + MyBatis+ JSP 圖書管理系統SSM
SpringBoot + MyBatis + html(LayUI) 酒店客房管理(可前后端分離)
SpringBoot + MyBatisPlus + html(Thymeleaf+bootstrap) 答題系統
SpringBoot + Spring Data JPA
項目一
SpringBoot+MyBatisPlus+Vue 倉庫管理(前后端分離)
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類型,在參數中就如Pageable對象```java
//根據作者和書名的關鍵字分頁查詢
Page<BookInfo> getAllByBookNameContainsOrBookAuthorContains(String nameKeyword,String authorKeyword,Pageable pageable);
Page<BookInfo> pageInfo = bookInfoDao.getAllByBookNameContainsOrBookAuthorContains("龍","山",PageRequest.of(0,5));
//分頁相關數據保存在pageInfo對象中
聚合函數分組查詢
自定義SQL
在數據訪問層接口中的方法上,可以加入@Query注解,默認要使用HQL(Hibernate專用)格式的語句。
如果要使用原生的SQL語句,需要添加nativeQuery=true屬性,用value屬性定義SQL語句
/** 在JPA中,如果要使用自定義的SQL語句* nativeQuery = true 開啟原生SQL語句* value="sql語句"* */
@Query(nativeQuery = true, value = "select book_author,count(book_id) from book_info group by book_author")
List testQuery();
@Test
void test(){List list = bookInfoDao.testQuery();//查詢的結果為集合,集合中保存的是每一行數據for (Object row : list) {//每一行頁數一個對象數組Object[] obj= (Object[])row;//根據索引得到查詢出的內容System.out.println(obj[0]+"---"+obj[1]);}
}
自定義SQL中帶參數
SQL語句中的":XXX"表示參數
如果方法的形參名和xxx一致時直接使用,如果不一致,在形參上加入@Param注解設置形參名
/** 根據作者查詢其圖書總庫存* 使用":形參名"在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 test(){List list = bookInfoDao.testQuery3("金庸");//查詢的結果為集合,集合中保存的是每一行數據for (Object row : list) {//每一行頁數一個對象數組Object[] obj= (Object[])row;//根據索引得到查詢出的內容System.out.println(obj[0]+"---"+obj[1]);}
}
關聯查詢
實體類
主表實體BookType
@Entity
@Data
public class BookType {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer typeId;private String typeName;
}
從表實體BookInfo
- 無需寫出外鍵字段屬性
- 額外添加外鍵字段對應的實體類對象屬性
@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_price")private Integer price;private Integer bookNum;private String publisherDate;//多對一查詢,以當前從表信息為主體,關聯相應的主表信息@JoinColumn(name = "type_id")//使用type_id字段進行多對一查詢@ManyToOne//多對一private BookType bt;
}
使用
-
多對一查詢,調用dao中的查詢方法,就會自動給外鍵字段對應的對象賦值
[外鏈圖片轉存中…(img-e6GbVH67-1676548026851)]
-
添加時,參數所需外鍵字段,使用外鍵字段對應的對象名代替
//添加 @PostMapping("/book") public RestResult<BookInfo> insert(BookInfo bookInfo) {return RestResult.ok("添加成功", bookInfoDao.save(bookInfo)); } -
如果根據外鍵字段查詢,要在從表dao中定義方法findBy外鍵對象名_外鍵對象屬性名(數據類型 外鍵字段)
//如果根據外鍵字段查詢,方法名寫為 By外鍵實體類屬性名_屬性名 List<BookInfo> getAllByBt_TypeId(Integer typeId);
前后端分離項目
前后端分離,就是將web應用中的前端頁面和后端代碼分開完成、部署。
- 前后端的開發者只需要完成各自的事情,最終以文檔的形式約定數據接口(URL、參數、返回值、請求方式)
- 前后端分別用獨立的服務器
- 后端只需處理數據并提供訪問接口(路徑),以RESTFul風格的JSON格式傳輸數據
- 前端只需負責渲染頁面和展示數據
傳統項目和前后端分離項目對比
傳統項目
前端和后端的代碼運行在一個服務器上,頁面經由控制器跳轉
SSM項目、圖書管理系統、答題系統
前后端分離項目
前后端的代碼分別運行在各自的服務器上
后端提供JSON格式字符串的數據接口
前端負責跳轉、解析JSON數據。
酒店客房管理系統
前后端分離項目后端控制層設計
請求方式設計:RESTFul風格
風格,不是標準,可以不用強制遵循。
RESTFul風格:用不同的請求方式去訪問同一個URL地址時,執行不同的操作。
特點
- 通過URL就能知道當前在哪個模塊
- 通過不同的請求方式決定執行什么操作
- 通過返回的狀態碼得到操作結果
使用RESTFul風格和普通方式對比
普通方式
localhost:8080/user/queryAll 查詢所有
localhost:8080/user/queryById?id=1001 條件查詢
localhost:8080/user/insert?name=ez&sex=男&age=20 添加
localhost:8080/user/update?name=ez&id=1001 修改
localhost:8080/user/delete?id=1001 刪除
RESTFul風格
localhost:8080/user 查詢所有get請求
localhost:8080/user/1001 條件查詢get請求
localhost:8080/user 添加post請求
localhost:8080/user 修改put請求
localhost:8080/user/1001 刪除delete請求
RESTFul風格具體使用
-
在請求映射的命名上,統一用小寫字母的名詞形式表示當前位于哪個模塊。如/user、/book_info
-
訪問時如果要傳參,使用"/模塊名/參數"方式,配合controller中的@PathVariable獲取
@GetMapping("/book/{id}") public BookInfo queryById(@PathVariable("id")Integer id){return service.findById(id); } -
在controller的方法上,使用@XXXMapping()設置訪問該方法的請求方式
- @GetMapping(“路徑”) 查詢
- @PostMapping(“路徑”) 添加
- **@PutMapping(“路徑”) ** 修改
- **@DeleteMapping(“路徑”) ** 刪除
- @RequestMapping(value=“路徑”,method=RequestMethod.GET/POST/PUT/DELETE))
-
如果請求方式不匹配,會報405異常
-
在同一個controller中,不能出現兩個請求方式和路徑都一致的方法
返回值設計
前后端分離項目的控制層方法的返回值也需要進行統一。
返回值通常包含以下信息
- 傳遞狀態,用狀態碼表示Integer code
- 傳遞消息,用字符串表示String msg
- 傳遞集合,用集合表示List list
- 傳遞對象,用對象表示Object obj
將這些信息封裝到一個對象中,這個對象稱為返回結果類RestResult對象
RestResult類具體設計
Postman
Postman API Platform | Sign Up for Free
Java技術棧
服務器端
? JavaSE(API、OOP)、JavaEE(Servlet、Spring、SpringMVC)
? tomcat
前端0
? HTML+CSS+JS+JQUERY、Bootstrap、LayUI、EasyUI、VUE
數據庫
? MySQL、Oracle、Redis
? JDBC、SpringJDBC、MyBatis、MyBatisPlus、JPA
工具
? IDEA、HBuilder、VSCode、Notepad++、Sublime、Navicat、Postman
JavaEE過程
Servlet + JDBC + JSP(bootstrap) 圖書商場
Spring + SpringMVC + SpringJDBC + JSP(bootstrap)
Spring + SpringMVC + MyBatis+ JSP 圖書管理系統SSM
SpringBoot + MyBatis + html(LayUI) 酒店客房管理(可前后端分離)
SpringBoot + MyBatisPlus + html(Thymeleaf+bootstrap) 答題系統
SpringBoot + Spring Data JPA
項目一
SpringBoot+MyBatisPlus+Vue 倉庫管理(前后端分離)
總結
以上是生活随笔為你收集整理的华清远见-重庆中心-框架个人总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【交互】【随机】Lost Root(CF
- 下一篇: The table(CF226D)