SpringData 完全入門指南

local0發表於2021-09-09

SpringData 筆記

1. 配置專案

1.pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns=""
         xmlns:xsi=""
         xsi:schemaLocation=" ">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lwen</groupId>
    <artifactId>SpringData</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--MySQL Driver-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>

        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>

        <!--spring-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>

        <!--spring data jpa-->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.8.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.3.6.Final</version>
        </dependency>

    </dependencies></project>

2.bean.xml

<?xml version="1.0" encoding="UTF-8"?><beans xmlns=""
       xmlns:xsi=""
       xmlns:context=""
       xmlns:tx=""
       xmlns:jpa=""
       xsi:schemaLocation=" /spring-beans.xsd
       /spring-jpa-1.3.xsd
       /spring-tx-4.0.xsd
       /spring-context-4.0.xsd">

    <!--1 配置資料來源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value=""/>
        <property name="url" value="jdbc:mysql://localhost:3306/spring_data"/>
    </bean>

    <!--2 配置EntityManagerFactory-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
        </property>
        <property name="packagesToScan" value="com.lwen"/>

        <property name="jpaProperties">
            <props>
                <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>

    </bean>

    <!--3 配置事務管理器-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <!--4 配置支援註解的事務-->
    <tx:annotation-driven/>

    <!--5 配置spring data-->
    <jpa:repositories base-package="com.lwen" entity-manager-factory-ref="entityManagerFactory"/>

    <context:component-scan base-package="com.lwen"/></beans>

3.實體類

package com.lwen.entry;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;/**
 * Student實體類
 */@Entitypublic class Student {  @Id@GeneratedValue
  private int id;  private String name;  private int age;  public int getId() {    return id;
  }  public void setId(int id) {    this.id = id;
  }  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;
  }    @Override
    public String toString() {        return "Student{" +                "id=" + id +                ", name='" + name + ''' +                ", age=" + age +                '}';
    }
}

這裡注意的一點就是我們在使用註解的時候一定要注意匯入的包我們都是匯入的javax中的類。

2.Repository

1. 使用

這個東西是SpringData的核心但是我們實際去看的時候會發現他是一個空介面也就是這個一個標記介面。我們自己的介面必須繼承這個介面才會具備查詢的功能所以說我們的自定的查詢器必須要繼承這個介面。這個介面是泛型介面也就是我們需要輸入兩個引數第一個就是我們查詢器的型別針對於那個表進行查詢另外一個就是表的Id的型別這個型別必須是序列化介面的子型別所以說不能使用基本型別我們只能使用包裝型別。

public interface EmployeeRepository extends Repository<Employee,Integer> {    Employee findByName(String name);
}

但是我們還有另外一種方式就是使用註解的方式不用繼承這個介面。

@RepositoryDefinition(domainClass = Student.class,idClass = Integer.class)public interface StudentRepository {
}

2. 常用的子介面

  • CrudRepository繼承Repository實現了CRUD相關的方法

  • PagingAndSortingRepository繼承CrudRepository實現了分頁排序相關的方法

  • JpaRepository繼承PagingAndSortingRepository實現JPA規範相關的方法

這些介面的功能都是非常強大並且實用的我們在使用的時候就可以直接繼承這些介面。

3.查詢規則

1.約定方法簽名查詢

圖片描述

圖片描述

2.手動查詢 @Query

1.複雜查詢

@Query("select o from Employee o where id = (select max(id) from Employee)")Employee getMaxIdEmployee();

2.佔位符查詢

@Query("select o from Employee o where name=?1 and age=?2")Employee getByNameAndAge(String name,Integer age);@Query("select o from Employee o where name like %?1%")Employee getByLikeName(String name);

3.命名引數

@Query("select o from Employee o where name=:name and age=:age")Employee getByNameAndAge1(@Param("name") String name,@Param("age") Integer age);

4.原生態查詢

@Query(nativeQuery = true,value = "select * from spring_data.employee where name = ?1")void getNative(String name)

3. 更新刪除操作

在SpringData中使用插刪改操作的時候我們必須定義一個Service層然後我們在Service層呼叫Dao的Repository來更新資料庫接著我們需要將那個Repository的方法設定為 @Modifying 最後最重要的就是我們在Service層的那個方法中寫 @Transactional 註解。才能更新成功所以所有的事務只能出現在 Service 層。但是注意因為我們的Service沒有繼承任何的Spring相關的東西我們要把它放到容器的時候需要使用@Service註解否則是不行的。

@Modifying@Query("update Employee o set o.name=:name where o.id=:id")void update(@Param("id") Integer id,@Param("name") String name);
@Servicepublic class EmployeeService {    @Autowired
    EmployeeRepository repository;    @Transactional //這個是javax裡面的
    public void update(){
        repository.update(1, "lwenxu");
    }
}

小提示很多時候我們發現有些東西在Spring中有在javax中也有我們優先匯入Javax中的如果出現了什麼方法無法呼叫估計就是包導錯了。

3. 常用的Repository

這三個高階的Repository實際上是從上到下依次繼承的。

1. CrudRepository

我們的Repository必須要繼承這個介面然後我們就有crud的一些操作了。接著我們需要建立service層然後再service中使用事務並且注入我們的Repository這裡我們用了一張新的表我們在bean上指定我們的表名就是使用@Table(name = "employee_test") 註解。最後進行save操作。

@Transactionalpublic void saveAll(List<Employee> employees){
    employeeCrudRepository.save(employees);
}
EmployeeCrudRepository employeeCrudRepository = ctx.getBean(EmployeeCrudRepository.class);
ArrayList<Employee> employees = new ArrayList<Employee>();for (int i = 0; i < 100; i++) {
    employees.add(new Employee(i, "lwen" + i, i));
}
employeeCrudRepository.save(employees);

2.PagingAndSortingRespository

他是分頁和排序功能。

EmployeePageAndSortRepository pageAndSort = ctx.getBean(EmployeePageAndSortRepository.class);//建立一個排序器是按照id的降續排列的Sort sort = new Sort(new Sort.Order(Sort.Direction.DESC, "id"));//第一個引數是當前的頁碼他是從0開始的//第二個引數就是每一頁的大小//第三個引數是可選引數傳入一個sort就是按照哪種方式分頁  由於這裡用的是降續所以出來的結果應該是 0 在最後一頁 99在第一頁Pageable pageable = new PageRequest(0, 9, sort);
Page<Employee> page = pageAndSort.findAll(pageable);//獲取當前頁的內容System.out.println(page.getContent());//獲取所有的頁數System.out.println(page.getTotalPages());

3.JpaRepository

  • findAll

  • save(entries)

  • deleteInBatch

  • findAll(sort)

  • flush

4. JpaSpecificationExecutor

這裡吧這個介面單獨拿出來說主要是因為這個介面實際上不是繼承自 Repository 這個介面他的作用從表面上貌似也是看不出來實際上我們前面看到我們可以進行簡單的分頁但是那些分頁並沒有一個讓我們傳入查詢條件的地方我們這個介面就是實現了這個功能也就是有條件的分頁查詢。

jpa介面需要繼承 JpaSpecificationExecutor 然後這裡就包含了五個方法分別是 findAll(三個過載) 和 findOne 以及 count 。

然後在使用的時候只呼叫對應的方法即可Page

new Specification<User>() {    public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {        return criteriaBuilder.gt(root.<Number>get("id"),4);
    }
}

可以看到這個函式式介面裡面的第一個引數是 root 這個相當於一個導航器也就是用它可以獲取到我們實體類中的屬性也就是我們獲取到表的欄位CriteriaQuery 則是可以進行語句的拼裝裡面有 where groupby 以及having 等方法進行sql組合的。二最後一個引數就是 CriteriaBuilder 用來建立 Predicate 物件的也就是生成查詢條件物件。

上面的程式生成的最後的條件就是獲取id大於4 的所有資訊然後分頁展示。

5. 表的對映關係

1.一對一

一對一關係這裡定義了一個Person物件以及一個IDCard物件

Person類

@Entity@Table(name="t_person")public class Person{    private int id;    private String name;    private IDCard card;    
    @OneToOne(mappedBy="person")  --->  指定了OneToOne的關聯關係mappedBy同樣指定由對方來進行維護關聯關係    public IDCard getCard()
    {        return card;
    }    public void setCard(IDCard card)
    {        this.card = card;
    }    @Id
    @GeneratedValue
    public int getId()
    {        return id;
    }    public void setId(int id)
    {        this.id = id;
    }    public String getName()
    {        return name;
    }    public void setName(String name)
    {        this.name = name;
    }
    
}

IDCard類

@Entity@Table(name="t_id_card")public class IDCard{    private int id;    private String no;    private Person person;    
    @Id
    @GeneratedValue
    public int getId()
    {        return id;
    }    public void setId(int id)
    {        this.id = id;
    }    public String getNo()
    {        return no;
    }    public void setNo(String no)
    {        this.no = no;
    }    @OneToOne  --->  OnetoOne指定了一對一的關聯關係一對一中隨便指定一方來維護對映關係這裡選擇IDCard來進行維護    @JoinColumn(name="pid")  --->  指定外來鍵的名字 pid    public Person getPerson()
    {        return person;
    }    public void setPerson(Person person)
    {        this.person = person;
    }
}

注意:在判斷到底是誰維護關聯關係時可以透過檢視外來鍵哪個實體類定義了外來鍵哪個類就負責維護關聯關係。

2.多對一

這裡我們定義了兩個實體類一個是ClassRoom一個是Student這兩者是一對多的關聯關係。

ClassRoom類

@Entity@Table(name="t_classroom")public class ClassRoom{    private Set<Student> students;    
    public ClassRoom()
    {
        students = new HashSet<Student>();
    }    
    public void addStudent(Student student)
    {
        students.add(student);
    }    @OneToMany(mappedBy="room")  --->  OneToMany指定了一對多的關係mappedBy="room"指定了由多的那一方來維護關聯關係mappedBy指的是多的一方對1的這一方的依賴的屬性(注意如果沒有指定由誰來維護關聯關係則系統會給我們建立一張中間表)    @LazyCollection(LazyCollectionOption.EXTRA)  --->  LazyCollection屬性設定成EXTRA指定了當如果查詢資料的個數時候只會發出一條 count(*)的語句提高效能    public Set<Student> getStudents()
    {        return students;
    }    public void setStudents(Set<Student> students)
    {        this.students = students;
    }
    
}

Student類

@Entity@Table(name="t_student")public class Student{    private ClassRoom room;    
    @ManyToOne(fetch=FetchType.LAZY)  ---> ManyToOne指定了多對一的關係fetch=FetchType.LAZY屬性表示在多的那一方透過延遲載入的方式載入物件(預設不是延遲載入)    @JoinColumn(name="rid")  --->  透過 JoinColumn 的name屬性指定了外來鍵的名稱 rid (注意如果我們不透過JoinColum來指定外來鍵的名稱系統會給我們宣告一個名稱)    public ClassRoom getRoom()
    {        return room;
    }    public void setRoom(ClassRoom room)
    {        this.room = room;
    }
}

3.多對多

多對多這裡通常有兩種處理方式一種是透過建立一張中間表然後由任一一個多的一方來維護關聯關係另一種就是將多對多拆分成兩個一對多的關聯關係

1. 不使用中間表的實體類

採用中間表的時候由任一一個多的一方來維護關聯關係

Teacher類

@Entity@Table(name="t_teacher")public class Teacher{    private int id;    private String name;    private Set<Course> courses;    
    public Teacher()
    {
        courses = new HashSet<Course>();
    }    public void addCourse(Course course)
    {
        courses.add(course);
    }    
    @Id
    @GeneratedValue
    public int getId()
    {        return id;
    }    public void setId(int id)
    {        this.id = id;
    }    public String getName()
    {        return name;
    }    public void setName(String name)
    {        this.name = name;
    }    @ManyToMany(mappedBy="teachers")  --->  表示由Course那一方來進行維護    public Set<Course> getCourses()
    {        return courses;
    }    public void setCourses(Set<Course> courses)
    {        this.courses = courses;
    }
    
}

Course類

@Entity@Table(name="t_course")public class Course{    private int id;    private String name;    private Set<Teacher> teachers;    
    public Course()
    {
        teachers = new HashSet<Teacher>();
    }    public void addTeacher(Teacher teacher)
    {
        teachers.add(teacher);
    }    @ManyToMany   ---> ManyToMany指定多對多的關聯關係    @JoinTable(name="t_teacher_course", joinColumns={ @JoinColumn(name="cid")}, 
    inverseJoinColumns={ @JoinColumn(name = "tid") })  --->  因為多對多之間會透過一張中間表來維護兩表直接的關係所以透過 JoinTable 這個註解來宣告name就是指定了中間表的名字JoinColumns是一個 @JoinColumn型別的陣列表示的是我這方在對方中的外來鍵名稱我方是Course所以在對方外來鍵的名稱就是 ridinverseJoinColumns也是一個 @JoinColumn型別的陣列表示的是對方在我這放中的外來鍵名稱對方是Teacher所以在我方外來鍵的名稱就是 tid    public Set<Teacher> getTeachers()
    {        return teachers;
    }    public void setTeachers(Set<Teacher> teachers)
    {        this.teachers = teachers;
    }    @Id
    @GeneratedValue
    public int getId()
    {        return id;
    }    public void setId(int id)
    {        this.id = id;
    }    public String getName()
    {        return name;
    }    public void setName(String name)
    {        this.name = name;
    }

}

2. 採用中間表實體類

把之前的ManyToMany拆分成兩個One-to-Many的對映

Admin類

@Entity@Table(name="t_admin")public class Admin{    private int id;    private String name;    private Set<AdminRole> ars;    public Admin()
    {
        ars = new HashSet<AdminRole>();
    }    public void add(AdminRole ar)
    {
        ars.add(ar);
    }    @Id
    @GeneratedValue
    public int getId()
    {        return id;
    }    public void setId(int id)
    {        this.id = id;
    }    public String getName()
    {        return name;
    }    public void setName(String name)
    {        this.name = name;
    }    @OneToMany(mappedBy="admin")  --->  OneToMany關聯到了AdminRole這個類由AdminRole這個類來維護多對一的關係mappedBy="admin"
    @LazyCollection(LazyCollectionOption.EXTRA)      public Set<AdminRole> getArs()
    {        return ars;
    }    public void setArs(Set<AdminRole> ars)
    {        this.ars = ars;
    }
}

Role類

@Entity@Table(name="t_role")public class Role{    private int id;    private String name;    private Set<AdminRole> ars;    public Role()
    {
        ars = new HashSet<AdminRole>();
    }    public void add(AdminRole ar)
    {
        ars.add(ar);
    }    @Id
    @GeneratedValue
    public int getId()
    {        return id;
    }    public void setId(int id)
    {        this.id = id;
    }    public String getName()
    {        return name;
    }    public void setName(String name)
    {        this.name = name;
    }    @OneToMany(mappedBy="role")  --->  OneToMany指定了由AdminRole這個類來維護多對一的關聯關係mappedBy="role"
    @LazyCollection(LazyCollectionOption.EXTRA)    public Set<AdminRole> getArs()
    {        return ars;
    }    public void setArs(Set<AdminRole> ars)
    {        this.ars = ars;
    }
}

AdminRole類

@Entity@Table(name="t_admin_role")public class AdminRole{    private int id;    private String name;    private Admin admin;    private Role role;    @Id
    @GeneratedValue
    public int getId()
    {        return id;
    }    public void setId(int id)
    {        this.id = id;
    }    public String getName()
    {        return name;
    }    public void setName(String name)
    {        this.name = name;
    }    @ManyToOne  --->  ManyToOne關聯到Admin    @JoinColumn(name="aid")      public Admin getAdmin()
    {        return admin;
    }    public void setAdmin(Admin admin)
    {        this.admin = admin;
    }    @ManyToOne  --->      @JoinColumn(name="rid")    public Role getRole()
    {        return role;
    }    public void setRole(Role role)
    {        this.role = role;
    }
}

小技巧:透過hibernate來進行插入操作的時候不管是一對多、一對一還是多對多都只需要記住一點在哪個實體類宣告瞭外來鍵就由哪個類來維護關係在儲存資料時總是先儲存的是沒有維護關聯關係的那一方的資料後儲存維護了關聯關係的那一方的資料

4.中間表

兩個實體tb_user,tb_role
現在我們再tb_user或者tb_role中任意一個裡面進行維護關係多對對的情況下我們需要建立一箇中間表來完成這個關係的對映我們再tb_user中新增註解@ManyToMany然後再新增一個註解@JoinTable因為我們是要建立中間表所以要使用這個註解。JoinTable註解中我們新增如下例子中的內容joinColumns當前表中的欄位在中間表中的欄位名稱inverseJoinColumns關聯的外來鍵表在中間表中的欄位名稱

@Entity@Table(name = "tb_user")@SequenceGenerator(name = "tb_user_sq",sequenceName = "tb_user_sqe")public class TbUser extends BaseEntity{/**
 * 使用者名稱
 */private String userName;/**
 * 登入名
 */private String loginName;/**
 * 登陸密碼
 */private String passWord;/**
 * 手機號
 */private String telPhone;/**
 * 一個使用者可以有多個角色
 */private List<TbRole> tbRoleList=new ArrayList<>();public String getUserName() {    return userName;
}public void setUserName(String userName) {    this.userName = userName;
}public String getLoginName() {    return loginName;
}public void setLoginName(String loginName) {    this.loginName = loginName;
}public String getPassWord() {    return passWord;
}public void setPassWord(String passWord) {    this.passWord = passWord;
}public String getTelPhone() {    return telPhone;
}public void setTelPhone(String telPhone) {    this.telPhone = telPhone;
}@Id@Override@GeneratedValue(generator = "tb_user_sq",strategy = GenerationType.SEQUENCE)public Long getId() {    return this.id;
}@ManyToMany(cascade = CascadeType.REMOVE,fetch = FetchType.LAZY)@JoinTable(name = "tb_user_role",joinColumns = @JoinColumn(name="tb_user_id",referencedColumnName = "id"),inverseJoinColumns = @JoinColumn(name = "tb_role_id",referencedColumnName = "id"))public List<TbRole> getTbRoleList() {    return tbRoleList;
}public void setTbRoleList(List<TbRole> tbRoleList) {    this.tbRoleList = tbRoleList;
}
}

因為在tb_user中我們維護了兩個表的關係所以如果我們在tb_role中如果不想建立關聯欄位的話就不用新增tbUser的關係欄位

@Entity@Table(name = "tb_role")@SequenceGenerator(name = "tb_role_sq",sequenceName = "tb_role_sqe")public class TbRole extends BaseEntity{@Override@Id@GeneratedValue(generator = "tb_role_sq",strategy = GenerationType.SEQUENCE)public Long getId() {    return this.id;
}private String roleName;@ManyToMany(mappedBy = "tbRoleList")public String getRoleName() {    return roleName;
}public void setRoleName(String roleName) {    this.roleName = roleName;
}

如果想要在tb_role中進行維護關聯欄位的話如下我們在欄位中新增註解 @ManyToMany然後使用直接屬性mappedBy值是當前表在關聯表中的欄位名稱

@Entity@Table(name = "tb_role")@SequenceGenerator(name = "tb_role_sq",sequenceName = "tb_role_sqe")public class TbRole extends BaseEntity{@Override@Id@GeneratedValue(generator = "tb_role_sq",strategy = GenerationType.SEQUENCE)public Long getId() {    return this.id;
}private String roleName;private List<TbUser> tbUserList=new ArrayList<>();public String getRoleName() {    return roleName;
}public void setRoleName(String roleName) {    this.roleName = roleName;
}@ManyToMany(mappedBy = "tbRoleList")public List<TbUser> getTbUserList() {    return tbUserList;
}public void setTbUserList(List<TbUser> tbUserList) {    this.tbUserList = tbUserList;
}

原文出處https://www.cnblogs.com/lwen/p/10135375.html  

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/132/viewspace-2819652/,如需轉載,請註明出處,否則將追究法律責任。

相關文章