SpringData 徹底入門指南

. 配置項目

1.pom.xml

<!--more-->java

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <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="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd      http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/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中的類。mysql

2.Repository

1. 使用

這個東西是SpringData的核心,可是咱們實際去看的時候會發現他是一個空接口,也就是這個一個標記接口。咱們本身的接口必須繼承這個接口才會具有查詢的功能,因此說咱們的自定的查詢器必需要繼承這個接口。這個接口是泛型接口也就是咱們須要輸入兩個參數,第一個就是咱們查詢器的類型,針對於那個表進行查詢,另一個就是表的Id的類型,這個類型必須是序列化接口的子類型,因此說不能使用基本類型,咱們只能使用包裝類型。spring

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

可是咱們還有另一種方式,就是使用註解的方式不用繼承這個接口。sql

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


2. 經常使用的子接口

  • CrudRepository:繼承Repository,實現了CRUD相關的方法
  • PagingAndSortingRepository:繼承CrudRepository,實現了分頁排序相關的方法
  • JpaRepository:繼承PagingAndSortingRepository,實現JPA規範相關的方法

這些接口的功能都是很是強大而且實用的咱們在使用的時候就能夠直接繼承這些接口。數據庫

3.查詢規則

1.約定方法簽名查詢


c3059e485ce4c9234bca85fff52bf193.jpeg



ca2f455497db82159306e720c4e65873.jpeg


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註解,不然是不行的。apache

@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其實是從上到下依次繼承的。app

1. CrudRepository

咱們的Repository必需要繼承這個接口,而後咱們就有crud的一些操做了。接着咱們須要建立service層,而後再service中使用事務,而且注入咱們的Repository,這裏咱們用了一張新的表咱們在bean上指定咱們的表名就是使用@Table(name = "employee_test") 註解。最後進行save操做。dom

@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

他是分頁和排序功能。maven

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<T> findAll(@Nullable Specification<T> var1, Pageable var2); 這個算是裏面最複雜的方法了主要是須要傳遞一個 Specification 對象,這個通常咱們直接傳遞一個匿名的對象便可,實現後的代碼以下:

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.多對一

這裏咱們定義了兩個實體類,一個是Cla***oom,一個是Student,這二者是一對多的關聯關係。

Cla***oom類:

@Entity@Table(name="t_cla***oom")public class Cla***oom{
    private Set<Student> students;
    
    public Cla***oom()
    {
        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 Cla***oom room;
    
    @ManyToOne(fetch=FetchType.LAZY)  ---> ManyToOne指定了多對一的關係,fetch=FetchType.LAZY屬性表示在多的那一方經過延遲加載的方式加載對象(默認不是延遲加載)
    @JoinColumn(name="rid")  --->  經過 JoinColumn 的name屬性指定了外鍵的名稱 rid (注意:若是咱們不經過JoinColum來指定外鍵的名稱,系統會給咱們聲明一個名稱)
    public Cla***oom getRoom()
    {
        return room;
    }
    public void setRoom(Cla***oom 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,因此在對方外鍵的名稱就是 rid,inverseJoinColumns也是一個 @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;}
相關文章
相關標籤/搜索