Spring Data JPA相關——多表聯合查詢中注解的使用


Spring Data JPA相關——多表聯合查詢中注解的使用

基本注解

@Entity

​ @Entity 用於定義對象將會成為被 JPA 管理的實體,將字段映射到指定的數據庫表中

@Table

​ 用於指定實體類對應的數據庫表名

public @interface Table { 
    //表的名字,可選。如果不填寫,系統認為好實體的名字一樣為表名。 
    String name() default ""; 
    //此表的catalog,可選 
    String catalog() default ""; 
    //此表所在schema,可選 
    String schema() default ""; 
    //唯一性約束,只有創建表的時候有用,默認不需要。 
    UniqueConstraint[] uniqueConstraints() default { }; 
    //索引,只有創建表的時候使用,默認不需要。
    Index[] indexes() default {}; }

@id

@Id 定義屬性為數據庫的主鍵,一個實體里面必須有一個,並且必須和 @GeneratedValue 配合使用和成對出現

@GeneratedValue

主鍵生成策略

public @interface GeneratedValue { 
    //Id的生成策略 
    GenerationType strategy() default AUTO; 
    //通過Sequences生成Id,常見的是Orcale數據庫ID生成規則,這個時候需要配合@SequenceGenerator使用 
    String generator() default ""; 
}

GenerationType 一共有以下四個值:

public enum GenerationType { 
    //通過表產生主鍵,框架借由表模擬序列產生主鍵,使用該策略可以使應用更易於數據庫移植。 		TABLE, 
    //通過序列產生主鍵,通過 @SequenceGenerator 注解指定序列名, MySql 不支持這種方式; 	SEQUENCE, 
    //采用數據庫ID自增長, 一般用於mysql數據庫 
    IDENTITY, 
    //JPA 自動選擇合適的策略,是默認選項; 
    AUTO 
}

@IdClass

@IdClass 利用外部類的聯合主鍵

作為復合主鍵類,要滿足以下幾點要求。

  • 必須實現 Serializable 接口。
  • 必須有默認的 public 無參數的構造方法。
  • 必須覆蓋 equals 和 hashCode 方法。equals 方法用於判斷兩個對象是否相同,EntityManger 通過 find 方法來查找 Entity 時,是根據 equals 的返回值來判斷的。本例中,只有對象的 name 和 email 值完全相同時或同一個對象時則返回 true,否 則返回 false。hashCode 方法返回當前對象的哈希碼,生成 hashCode 相同的概率越小越好,算法可以進行優化。

@Basic

@Basic 表示屬性是到數據庫表的字段的映射。如果實體的字段上沒有任何注解,默認即為 @Basic。

@Transient

@Transient 表示該屬性並非一個到數據庫表的字段的映射,表示非持久化屬性。JPA 映射數據庫的時候忽略它,與 @Basic 相反的作用

@Column

@Column 定義該屬性對應數據庫中的列名

public @interface Column { 
    //數據庫中的表的列名;可選,如果不填寫認為字段名和實體屬性名一樣。 
    String name() default ""; 
    //是否唯一。默認flase,可選。
    boolean unique() default false; 
    //數據字段是否允許空。可選,默認true。 
    boolean nullable() default true; 
    //執行insert操作的時候是否包含此字段,默認,true,可選。
    boolean insertable() default true; 
    //執行update的時候是否包含此字段,默認,true,可選。
    boolean updatable() default true; 
    //表示該字段在數據庫中的實際類型。 
    String columnDefinition() default ""; 
    //數據庫字段的長度,可選,默認255 
    int length() default 255; 
}

@Temporal

@Temporal 用來設置 Date 類型的屬性映射到對應精度的字段。

  • @Temporal(TemporalType.DATE)映射為日期 // date (只有日期)
  • @Temporal(TemporalType.TIME)映射為日期 // time (是有時間)
  • @Temporal(TemporalType.TIMESTAMP)映射為日期 // date time (日期+時間)

@Lob

@Lob 將屬性映射成數據庫支持的大對象類型,支持以下兩種數據庫類型的字段

  • Clob(Character Large Ojects)類型是長字符串類型,java.sql.Clob、Character[]、char[] 和 String 將被映射為 Clob 類 型。
  • Blob(Binary Large Objects)類型是字節類型,java.sql.Blob、Byte[]、byte[]和實現了 Serializable 接口的類型將被映射為 Blob 類型。
  • 由於 Clob,Blob 占用內存空間較大一般配合 @Basic(fetch=FetchType.LAZY) 將其設置為延遲加載。

多表關聯注解

@JoinColumn

定義外鍵關聯的字段名稱

@JoinColumn 主要配合 @OneToOne、@ManyToOne、@OneToMany 一起使用,單獨使用沒有意義。

@JoinColumn 可以定義多個字段的關聯關系。

@OneToOne

一對一關聯關系

用法 @OneToOne 需要配合 @JoinColumn 一起使用。注意:可以雙向關聯,也可以只配置一方,看實際需求

源碼:

public @interface OneToOne { 
    //關系目標實體,非必填,默認該字段的類型。 
    Class targetEntity() default void.class; 
    //cascade 級聯操作策略 
    /* 
    1. CascadeType.PERSIST 級聯新建 
    2. CascadeType.REMOVE 級聯刪除 
    3. CascadeType.REFRESH 級聯刷新 
    4. CascadeType.MERGE 級聯更新 
    5. CascadeType.ALL 四項全選 
    6. 默認,關系表不會產生任何影響 */ 
    CascadeType[] cascade() default {}; 
    //數據獲取方式EAGER(立即加載)/LAZY(延遲加載) 
    FetchType fetch() default EAGER; 
    //是否允許為空 
    boolean optional() default true; 
    //關聯關系被誰維護的。 非必填,一般不需要特別指定。 
    //注意:只有關系維護方才能操作兩者的關系。被維護方即使設置了維護方屬性進行存儲也不會更新外鍵關聯。1)mappedBy不 能與@JoinColumn或者@JoinTable同時使用。2)mappedBy的值是指另一方的實體里面屬性的字段,而不是數據庫字段,也不是 實體的對象的名字。既是另一方配置了@JoinColumn或者@JoinTable注解的屬性的字段名稱。 String mappedBy() default ""; 
    //是否級聯刪除。和CascadeType.REMOVE的效果一樣。兩種配置了一個就會自動級聯刪除 			boolean orphanRemoval() default false; 
}

案例:假設一個學生對應一個班級,添加學生的同時添加班級,Student類的內容如下:

@OneToOne(cascade = CascadeType.PERSIST) 
//關聯的外鍵字段 
@JoinColumn(name = "grade_id") 
private Grade grade;

如果需要雙向關聯,Grade類的內容如下:

@OneToOne(mappedBy = "grade") 
private Student student;

Student實體類

@Entity
@Data
@Table(name = "t_student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String studentname;
    //一對一關聯
    @OneToOne(cascade = CascadeType.PERSIST)
    //關聯的外鍵字段
    @JoinColumn(name = "grade_id")
    private Grade grade;

    private String sex;

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", studentname='" + studentname + '\'' + ",grade=" + this.grade.getGradename() +
                '}';
    }
}

Grade實體類

@Data
@Entity
@Table(name = "t_grade")
public class Grade {


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String gradename;

    //一對一
    @OneToOne(mappedBy = "grade")
    private Student student;

    @Override
    public String toString() {
        return "Grade{" +
                "id=" + id +
                ", gradename='" + gradename + '\'' +
                '}';
    }
}

StudentRepository接口

public interface StudentRepository extends JpaRepository<Student, Integer> {
}

測試類

@SpringBootTest
public class OneToOneTest {

    @Autowired
    private StudentRepository studentRepository;

    @Test
    public void testOneToOne(){
        //創建年級對象
        Grade grade = new Grade();
        grade.setGradename("一年級");

        //創建學生對象
        Student student = new Student();
        student.setStudentname("張浩");
        student.setSex("男");

        //設置關聯關系
        student.setGrade(grade);
        //保存
        studentRepository.save(student);

    }

    @Test
    public void testSearch(){
        Optional<Student> student = studentRepository.findById(1);
        System.out.println("學生信息:"+student);
        System.out.println("年級信息:"+student.get().getGrade()); 
    }
}

@OneToMany 一對多 & @ManyToOne 多對一

@OneToMany源碼語法

public @interface OneToMany { 
    Class targetEntity() default void.class; 
    //cascade 級聯操作策略:(CascadeType.PERSIST、CascadeType.REMOVE、CascadeType.REFRESH、 CascadeType.MERGE、CascadeType.ALL) 如果不填,默認關系表不會產生任何影響。 CascadeType[] cascade() default {}; 
    //數據獲取方式EAGER(立即加載)/LAZY(延遲加載) FetchType fetch() default LAZY; 
    //關系被誰維護,單項的。注意:只有關系維護方才能操作兩者的關系。 String mappedBy() default ""; 
    //是否級聯刪除。和CascadeType.REMOVE的效果一樣。兩種配置了一個就會自動級聯刪除 			boolean orphanRemoval() default false; 
}

public @interface ManyToOne { 
    Class targetEntity() default void.class;
    CascadeType[] cascade() default {}; 
    FetchType fetch() default EAGER; 
    boolean optional() default true; 
}

創建User實體類

  1. 多個用戶擁有同一個角色(多對一)

  2. toString()方法不添加Roles屬性

  3. 添加無參構造方法及帶參(用戶名,角色類型)構造方法

@Entity
@NoArgsConstructor
@Table(name = "t_users")
public class User {

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String username;

    public User(String username) {
        this.username = username;
    }

    //多個用戶擁有同一個角色
    @ManyToOne
    @JoinColumn(name = "roles_id")
    private Role role;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Role getRole() {
        return role;
    }

    public void setRole(Role role) {
        this.role = role;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                '}';
    }
}

創建Role實體類

  1. 一個角色下有多個用戶(一對多)

  2. toString()方法不添加users屬性

  3. 添加無參構造方法及帶參(角色名稱)構造方法

@Entity
@NoArgsConstructor
@Table(name = "t_roles")
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String rolename;

    public Role(String rolename) {
        this.rolename = rolename;
    }

    //一個角色被多個用戶擁有
    // fetch = FetchType.EAGER:立即加載
    @OneToMany(mappedBy = "role",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
    private Set<User> users = new HashSet<User>();

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRolename() {
        return rolename;
    }

    public void setRolename(String rolename) {
        this.rolename = rolename;
    }

    public Set<User> getUsers() {
        return users;
    }

    public void setUsers(Set<User> users) {
        this.users = users;
    }

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", rolename='" + rolename + '\'' +
                '}';
    }
}

創建RolesRepository接口

@Repository
public interface RoleRepository extends JpaRepository<Role, Integer> {
}

測試類

@SpringBootTest
public class test {

    @Autowired
    private RoleRepository roleRepository;

    /*** 級聯添加:添加角色同時添加用戶 */
    @Test
    public void test1(){
        //創建角色對象
        Role role = new Role("經理");
        //創建兩個用戶
        User user1 = new User();
        user1.setUsername("王五");
        user1.setRole(role);
        User user2 = new User();
        user2.setUsername("趙四");
        user2.setRole(role);
        //設置關聯關系
        role.getUsers().add(user1);
        role.getUsers().add(user2);

        //級聯保存用戶
        roleRepository.save(role); 
    }
    //級聯查詢
    @Test
    @Transactional
    public void test2(){
        Role role = roleRepository.getOne(1);
        System.out.println(role.getRolename());
        Set<User> users = role.getUsers();
        users.forEach(System.out::println);
    }
}

@JoinTable 關聯關系表

@JoinTable 是指如果對象與對象之間有個關聯關系表的時候,就會用到這個,一般和 @ManyToMany 一起使用。

用法

假設 Blog 和 Tag 是多對多的關系,有個關聯關系表 blog_tag_relation ,表中有兩個屬性 blog_id 和 tag_id ,那么 Blog 實

體里面的寫法如下:

@Entity public class Blog{ 
    @ManyToMany 
    @JoinTable( name="blog_tag_relation", joinColumns=@JoinColumn(name="blog_id",referencedColumnName="id"), inverseJoinColumn=@JoinColumn(name="tag_id",referencedColumnName="id") 
               private List<Tag> tags = new ArrayList<Tag>(); ) 
}

@ManyToMany 多對多

@ManyToMany 表示多對多,和 @OneToOne、@ManyToOne 一樣也有單向雙向之分,單項雙向和注解沒有關系,只看實體類之 間是否相互引用。 主要注意的是當用到 @ManyToMany 的時候一定是三張表,不要想着偷懶,否則會發現有很多麻煩

多對多的關聯關系案例

需求:一個項目由多個員工負責,一個員工參與多個項目(多對多關系)--給項目分配員工

員工:多方

項目:多方

創建Project實體類
  1. 一個項目由有多個員工負責(一對多)

  2. toString()方法不添加Employee屬性

  3. 添加無參構造方法及帶參(項目名稱)構造方法

@NoArgsConstructor
@Entity
@Table(name = "t_projects")
public class Project {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer projectId;

    private String projectName;

    public Project(String projectName) {
        this.projectName = projectName;
    }

    /* 配置項目到員工的多對多關系
     * * 配置多對多的映射關系
     * * 1.聲明表關系的配置
     * * @ManyToMany(targetEntity = Employee.class)
     * //多對多 * targetEntity:代表對方的實體類字節碼
     * * 2.配置中間表(包含兩個外鍵)
     * * @JoinTable
     * * name : 中間表的名稱
     * * joinColumns:配置當前對象在中間表的外鍵
     * * @JoinColumn的數組
     * * name:外鍵名
     * * referencedColumnName:參照的主表的主鍵名
     * * inverseJoinColumns:配置對方對象在中間表的外鍵
     *
     * */
    @OrderBy("employee_name DESC")
    @ManyToMany(targetEntity = Employee.class, cascade = CascadeType.ALL)
    //第三張表(外鍵關系表、中間表)
    // name屬性:第三張表的表名稱
    @JoinTable(name = "t_employee_project",
            //在中間表的主鍵名稱,當前類的主鍵名稱
            joinColumns = @JoinColumn(name = "project_id",  referencedColumnName = "projectId"),
            //對象類在中間表的主鍵名稱,當前類的主鍵名稱
            inverseJoinColumns = @JoinColumn(name = "employee_id", referencedColumnName = "empId")
    )
    private Set<Employee> employees = new HashSet<>();

    public Integer getProjectId() {
        return projectId;
    }

    public void setProjectId(Integer projectId) {
        this.projectId = projectId;
    }

    public String getProjectName() {
        return projectName;
    }

    public void setProjectName(String projectName) {
        this.projectName = projectName;
    }

    public Set<Employee> getEmployees() {
        return employees;
    }

    public void setEmployees(Set<Employee> employees) {
        this.employees = employees;
    }

    @Override
    public String toString() {
        return "Project{" +
                "projectId=" + projectId +
                ", projectName='" + projectName + '\'' +
                '}';
    }
}
創建Employee實體類
  1. 一個員工負責多個項目(一對多)

  2. toString()方法不添加Project屬性

  3. 添加無參構造方法及帶參(員工名稱)構造方法

@NoArgsConstructor
@Entity
@Table(name = "t_employees")
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer empId;

    private String employeeName;

    public Employee(String employeeName) {
        this.employeeName = employeeName;
    }

    //多對多放棄維護權:被動的一方放棄
    @ManyToMany(mappedBy = "employees")
    private Set<Project> projects = new HashSet<>();

    public Integer getEmpId() {
        return empId;
    }

    public void setEmpId(Integer empId) {
        this.empId = empId;
    }

    public String getEmployeeName() {
        return employeeName;
    }

    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "empId=" + empId +
                ", employeeName='" + employeeName + '\'' +
                '}';
    }
}
創建ProjectRepository接口
public interface ProjectRepository extends JpaRepository<Project,Integer> { 
}
測試多對多
  • 創建2個項目(超市管理系統、酒店管理系統),3個員工(張三、李四、王五)
  • 張三、李四、王五負責超市管理系統
  • 張三、李四同時負責酒店管理系統
/*** 級聯添加:添加角色同時添加用戶 */ 
@Test 
//以下兩個注解必須提供 
@Transactional
//開啟事務 
@Rollback(false)
//取消回滾 
public void testAdd() { 
    //創建兩個項目對象 
    Project project1 = new Project("超市管理系統"); 
    Project project2 = new Project("酒店管理系統"); 
    //創建三個員工對象 
    Employee employee1 = new Employee("張三"); 
    Employee employee2 = new Employee("李四"); 
    Employee employee3 = new Employee("王五"); 
    //設置關聯關系 
    //給超市管理系統分配員工 
    project1.getEmployees().add(employee1); 			 	      	    project1.getEmployees().add(employee2); project1.getEmployees().add(employee3); 
    //給酒店管理系統分配員工 
    project2.getEmployees().add(employee1);    project2.getEmployees().add(employee2); 
    //保存 
    projectRepository.save(project1); 
    projectRepository.save(project2); 
}

必須在事務環境中運行,否則會出現 detached entity passed to persist 錯誤

結果

生成的t_employee_project表

image

生成的t_employees表

image

生成的t_projects表

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM