华清远见-重庆中心-框架阶段

框架

一套规范。

实际是他人实现的一系列接口和类的集合。通入导入对应框架的jar文件(maven项目导入对应的依赖),进行适当的配置,就能使用其中的所有内容。

开发者可以省去很多模板代码,如dao中的CRUD,MVC模式下层与层之间的关联。只需要集中精力实现项目中的业务逻辑部分。

Java主流框架

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框架。是一个管理项目中对象的容器,同时也是其他框架的粘合器,目的就是对项目进行解耦。

轻量级:对原有代码的侵入很小。

img

Spring的核心是IOC控制反转和AOP面向切面编程

组成

img

名词解释

IOC

Inversion Of Control 控制反转

DI

Dependency Injection 依赖注入

举例说明

用代码描述场景:员工食堂每天提供事物

  • 食物:米饭类

    public class Rice{
        public Rice(){
            sout("今天吃米饭");
        }
    }
    
  • 食物:面条类

    public class Noodles{
        public Noodles(){
            sout("今天吃面条");
        }
    }
    
  • 厨师类

    public class Cook{
        //定义一个做饭的方法,做什么饭new什么对象,创建食物对象的权限,是由当前厨师类决定。
        public void cooking(){
            //new Rice();
            new Noodles();
        }
    }
    
  • 员工:main方法

    psvm(){
        Cook cook = new Cook();
        cook.cooking();
    }
    

这种方式,Cook类中的cooking()方法创建什么对象,就输出什么内容。(厨师做什么,员工就吃什么)。

如果有人想要吃指定食物,就要修改源代码cooking()方法中创建的对象。

解决方案:定义一个接口:Food

public interface Food{
	void info();
}

让原本的所有食物Rice类和Noodles类实现该接口

public class Rice implements Food{
    @Override
    public void info(){
        sout("今天吃米饭");
    }
}
public class Noodles implements Food{
    @Override
    public void info(){
        sout("今天吃面条");
    }
}

给厨师Cook类中cooking()方法定义一个参数:Food接口

public class Cook{
    public void cooking(Food food){
        food.info();
    }
}

这时Cook对象调用cooking()方法时,需要提供一个Food接口类型的实现类。

psvm(){
    Cook cook = new Cook();
    //传递什么参数,旧调用该参数重写后的方法
    cook.cooking(new Rice());
    cook.cooking(new Noodles());
}

整个过程中,将创建什么食物对象的控制权,由厨师交给用户,这就是控制反转(IOC)。

厨师对象调用cooking()方法的参数,就是对于食物Food对象的依赖,是由用户注入进来的,这就是依赖注入(DI)。

这样一来,各个对象之间互相独立(没有在某个类中new另一个类的对象),降低了代码之间的耦合度。

总结:控制反转(IOC)是一种思想,就是让创建对象的控制权由自身交给第三方,控制反转这种思想,通过依赖注入(DI)的方式实现。

IOC和DI其实都是在描述控制反转,IOC是思想,DI是具体实现方式。

这里的第三方,就是Spring。Spring是一个容器,可以管理所有对象的创建和他们之间的依赖关系。

可以理解为:“Spring就是用来管理对象的,在需要用到某个对象的时候,帮我们自动创建”。

如Servlet+JSP模式写Web项目时,会在控制层Servlet中创建业务逻辑层Service对象,在Service层中创建数据访问层Dao对象。

有了Spring后,就不会出现new这些对象的代码了。

Spring需要导入对应的jar文件后,定义一个配置文件,在该配置文件中配置程序运行过程中所需的对象。

AOP

Aspect Orintend Programming 面向切面编程

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配置文件

创建一个Spring的配置文件,在其中注入上一步创建的类的对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F5ds9LWh-1676442546356)(D:\框架\Spring01.assets\image-20230112110130552.png)]

在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();
    }
}

思考

  • 1.对象在什么时候创建?

    默认情况下,在解析Spring配置文件的时候,自动创建一个对象。

  • 2.如果不想在解析时自动创建怎么办?

    在配置文件的某个<bean>标签中,添加一个"lazy-init=true"属性,表示该对象设置为懒加载,

    在初始化Spring容器时不会创建对象,只有在调用getBean()时才会创建对象

  • 3.如果设置为懒加载,是不是每次调用getBean(),都会创建一个对象呢?

    默认情况下,Spring容器只会创建一个对象。

    在配置文件的某个<bean>标签中,添加一个"scope=‘prototype’"属性,表示每次调用getBean()方法,就会创建一个对象。

    该属性的值默认为"singleton",表示单例模式,只会创建一个对象。

总结:默认情况下,通过<bean>标签定义的类,在Spring容器初始化时,创建一个对象。

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:ss
    private 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;

    @Override
    public 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;

    @Override
    public 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这个id
    public 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项目中,可以利用自动装配,在控制层中自动装配业务逻辑层的对象,在业务逻辑层中自动装配数据访问层的对象。

配置文件

<!--省略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 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-r8Cg1hfr-1676442851783)(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 {


    @Autowired
    private BookInfoDao bookInfoDao;

    @Test
    void contextLoads() {
        List<BookInfo> all = bookInfoDao.findAll();
        all.forEach(System.out::println);
    }

    @Test
    void 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);
    }

    @Test
    void 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);
    }
    @Test
    void delete() {
        //根据主键值删除,如果值不存在,会报错
        //bookInfoDao.deleteById(36);

        //根据对象删除,如果对象中包含主键值则删除,如果没有值或不存在,不会报错
        BookInfo bookInfo = new BookInfo();
        //bookInfo.setBookId(330);
        bookInfoDao.delete(bookInfo);
    }


    @Test
    void 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/AndfindByBookNameOrBookAuthor(String name,String author)book_name = name or book_author = author
After/BeforfindByBookPriceAfter(double price)book_price > price
GreaterThanEqual/LessThanEqualfindByBookNumLessThanEqual(int num)book_num <= num
BetweenfindByBookNumBetween(int min,int max)book_num between min and max
Is[Not]NullfindByPublisherDateIsNull()publish_date is null
[Not]LikefindByBookNameLike(String condition)book_name like ‘condition’
[Not]ContainsfindByBookNameContains(String keyword)book_name like ‘%keyword%’
StartsWith/EndsWithfindByBookNameStartsWith(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/GreaterThan
    List<BookInfo> findAllByPriceAfter(int price);

    //查询价格小于于指定值    字段对应的属性名  Before/LessThan
    List<BookInfo> findAllByPriceLessThan(int price);


    //查询库存大于等于指定值 GreaterThanEqual
    List<BookInfo> queryAllByBookNumGreaterThanEqual(int num);

    //查询库存在指定闭区间内 Between(int min,int max)
    List<BookInfo> findAllByBookNumBetween(int min,int max);


    //空值查询 null
    //查询出版日期为空  IsNull/IsNotNull
    List<BookInfo> findAllByPublisherDateIsNull();

    //书名中带有关键字 Like/NotLike 实参一定要使用%或_
    List<BookInfo> getAllByBookNameLike(String keyword);

    //作者名中带有关键字  Contains/NotContains 实参只需要关键字
    List<BookInfo> getAllByBookAuthorContains(String keyword);

    //指定作者的姓   指定开头/结尾  StartsWith/EndsWith
    List<BookInfo> getAllByBookAuthorStartsWith(String keyword);


    //查询所有数据,按价格降序    无条件排序 OrderBy字段[Desc/Asc]
    List<BookInfo> getAllByOrderByPriceDesc();

    //查询指定类型,按id降序
    List<BookInfo> getAllByTypeIdOrderByBookIdDesc(Integer typeId);

}

条件分页查询

只需在定义方法时,将方法的返回值设置为Page类型,在参数中就如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-enMZRXyj-1676442851784)(day16day17-SpringBoot+JPA.assets/image-20230214105234875.png)]

从表book_info

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-COMeKRsJ-1676442851784)(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-i0uAebfp-1676442851785)(day16day17-SpringBoot+JPA.assets/image-20230214111732161.png)]

  • 添加时,参数所需外键字段,使用外键字段对应的对象名代替

    //添加
    @PostMapping("/book")
    public RestResult<BookInfo> insert(BookInfo bookInfo) {
        return RestResult.ok("添加成功", bookInfoDao.save(bookInfo));
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hNnDpRnL-1676442851787)(day16day17-SpringBoot+JPA.assets/image-20230214113125279.png)]

  • 如果根据外键字段查询,要在从表dao中定义方法findBy外键对象名_外键对象属性名(数据类型 外键字段)

    //如果根据外键字段查询,方法名写为 By外键实体类属性名_属性名
    List<BookInfo> getAllByBt_TypeId(Integer typeId);
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值