(Reprint) SpringData JPA advanced - JPA one to many, many to many

Link: https://blog.csdn.net/qq_36662478/article/details/89111437

 

Primary key generation strategy in JPA

                              .
   four standard usages provided by JPA are TABLE,SEQUENCE,IDENTITY,AUTO\color{red}{TABLE,SEQUENCE,IDENTITY,AUTO}TABLE,SEQUENCE,IDENTITY,AUTO. Since we use the hibernate implementation, it also supports the generation rules defined in hibernate.

1.1 four generation rules in JPA

1.1.1 IDENTITY: the primary key is automatically generated by the database (mainly automatic growth)

Usage:

@Id 
@GeneratedValue(strategy = GenerationType.IDENTITY) 
private Long custId;

1.1.2 SEQUENCE: generate the primary key according to the sequence of the underlying database, provided that the database supports the sequence

Usage:

@Id 
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator="payablemoney_seq") 
@SequenceGenerator(name="payablemoney_seq", sequenceName="seq_payment") 
private Long custId;

//@Definition in the source code of SequenceGenerator 
@Target({TYPE, METHOD, FIELD}) 
@Retention(RUNTIME) 
public @interface SequenceGenerator { 
    //Represents the name of the table's primary key generation policy, which is referenced in the "generator" value set in @ GeneratedValue 
    String name();
    //Property indicates the name of the database sequence used to generate the policy. 
    String sequenceName() default ""; 
    //Indicates the initial primary key value, which is 0 by default 
    int initialValue() default 0; 
    //Indicates the size of each increase in the primary key value. For example, if you set 1, it means that 1 will be added automatically every time a new record is inserted. The default value is 50 
    int allocationSize() default 50; 
}

1.1.3 TABLE: use a specific database table to save the primary key

Usage:

@Id 
@GeneratedValue(strategy = GenerationType.TABLE, generator="payablemoney_gen") 
@TableGenerator(name = "pk_gen", table="tb_generator", pkColumnName="gen_name", valueColumnName="gen_value", pkColumnValue="PAYABLEMOENY_PK", allocationSize=1 ) 
private Long custId;

//@Definition of TableGenerator: 
@Target({TYPE, METHOD, FIELD}) 
@Retention(RUNTIME) 
public @interface TableGenerator { 
    //Represents the name of the table's primary key generation policy, which is referenced in the "generator" value set in @ GeneratedValue 
    String name(); 
    //Represents the name of the table persisted by the table generation policy. For example, the table here uses "tb_generator" in the database. 
    String table() default ""; 
    //catalog and schema specify the directory name or database name of the table 
    String catalog() default ""; 
    String schema() default ""; 
    //The value of the property indicates the name of the key value corresponding to the primary key generation policy in the persistence table. For example, in TB generator, use Gen name as the key value of the primary key 
    String pkColumnName() default ""; 
    //The value of the property represents the current value generated by the primary key in the persistence table, and its value will be accumulated with each creation. For example, in TB generator, use Gen value as the primary key value 
    String valueColumnName() default ""; 
    //The value of property indicates the primary key corresponding to the generation policy in the persistence table. For example, in the TB generator table, set the value of Gen name to customer PK.
    String pkColumnValue() default ""; 
    //Indicates the initial primary key value, which is 0 by default. 
    int initialValue() default 0; 
    //Indicates the size of each increase in the primary key value. For example, if it is set to 1, it means that 1 will be added automatically every time a new record is created. The default value is 50. 
    int allocationSize() default 50; 
    UniqueConstraint[] uniqueConstraints() default {}; 
} 

Here, the application table tb ﹣ generator is defined as:

CREATE TABLE tb_generator ( 
    id NUMBER NOT NULL, 
    gen_name VARCHAR2(255) NOT NULL, 
    gen_value NUMBER NOT NULL, 
    PRIMARY KEY(id) 
)

1.1.4 AUTO: select one of the above three (the default is TABLE)

AUTO policy is the default policy of JPA, which is defined in hibernate code GenerationType.AUTO. Using AUTO policy is to give the policy generated by the primary key to the persistence engine, which chooses the most appropriate one from Table policy, Sequence policy and Identity policy.

@Id 
@GeneratedValue(strategy = GenerationType.AUTO) 
private Long custId;

1.2 use the primary key generation rules in hibernate

1.2.1 primary key generation rules provided in Hibernate

   before explaining Hibernate's primary key generation strategy, first understand two concepts, namely, natural primary key and proxy primary key, as follows:

   natural primary key \ color{red} {natural primary key} natural primary key:
                     . For example, in the customer table, if the name field is used as the primary key, the precondition must be: the name of each customer cannot be null, the customer name cannot be duplicate, and the customer name cannot be modified. Although this is also feasible, it can not meet the changing business needs. Once there is a business demand that allows customers to rename, it is necessary to modify the data model and redefine the primary key of the table, which increases the difficulty of database maintenance.

   proxy primary key \ color{red} {proxy primary key} proxy primary key:
                      . This field is generally named "ID" and is usually of the integer type, because the integer type saves more database space than the string type. In the above example, it is obviously more reasonable to use a proxy primary key.

1.2.2 usage

@Entity 
@Table(name="cst_customer") 
public class Customer implements Serializable { 
    @Id 
    @Column(name="cust_id") 
    //The generator property references the value of the @ GenericGenerator annotation name property 
    @GeneratedValue(generator="uuid") 
    //@The generic generator annotation is provided by hibernate. 
    //The strategy property specifies the build rules provided in hibernate 
    //The name attribute is used to give a name to the used build rule for reference by JPA 
    @GenericGenerator(name="uuid",strategy="uuid") 
    private String custId; 
}

2. The first level cache and snapshot mechanism in JPA

2.1 first level cache in JPA

@Test 
// Prove the existence of L1 cache: 
public void demo1(){ 
    EntityManager em = JPAUtils.createEntityManager(); 
    EntityTransaction tx = em.getTransaction(); 
    tx.begin(); 
    Customer customer1 = em.find(Customer.class, 1l);
    // An sql query for customer No.1 immediately occurs, and the data is stored in the first level cache 
    System.out.println(customer1); 
    Customer customer2 = em.find(Customer.class, 1l);
    // No SQL statement fetched data from L1 cache 
    System.out.println(customer2); 
    System.out.println(customer1 == customer2);
    // true level 1 cache is the address of the object 
    tx.commit(); 
    em.close(); 
}

2.2 snapshot mechanism in JPA (for cache synchronization)

  JPA When the data is put into the primary cache, a copy of the data is copied and put into the snapshot. When the commit() method is used to commit the transaction, the primary cache will be cleaned up at the same time. At this time, the value of the primary key field will be used to determine whether the objects in the primary cache are consistent with the objects in the snapshot. If the properties of the two objects change, the update statement will be executed to synchronize the contents of the cache with the data Library, and update the snapshot; if consistent, the update statement is not executed.
The function of   snapshot is to ensure that the data in the first level cache is consistent with the data in the database.

Program code:

@Test
  public void test2(){
    Customer c2 = null;
    //An object in memory
    //1. Get object
    EntityManager em = JPAUtil.getEntityManager();
    //2. Open transaction
    EntityTransaction tx = em.getTransaction();
    tx.begin();
    //3. Execute query: query the customer object with ID 5
    c2 = em.find(Customer.class, 5L);
    System.out.println(c2);
    //custName:Hibernate CRUD persist
    //Change the name of the client: Amendment building
    c2.setCustName("Amendment building");
    System.out.println(c2);
    //custName: Amendment building
    //4. Submission
    tx.commit();
    //5. Close
    em.close();
    //When the program goes here, the first level cache is gone, but it doesn't affect me to continue to use the client object
    System.out.println(c2);
    //What exactly is custName
  }

Question:
What exactly is the output of   custName?

analysis:
                      . If the output is correction block, it means that the data in our program memory may be inconsistent with the data in the database table, which is dirty data.

reflection:
Is it possible to output the correction building, and the data in the database also becomes the correction building? If this happens, how can it be done?

answer:

   JPA's snapshot mechanism (in fact, hibernate's snapshot mechanism).

3 multi meter design

3.1 division of relationship between tables

There are three relationships between multiple tables in the database, as shown in the figure.


                               . Note: there are two kinds of one to many relationships: one to many and many to one. So four are more accurate.
Clarify:
                       . The one-to-one situation is almost not used in actual development.

3.2 analysis steps of table relationship in JPA framework

                        . In this framework (such as JPA), we can operate on database tables by operating entity classes. So today our focus is to master the relationship between configuration entities.

Step 1: first determine the relationship between the two tables. (if the relationship is determined to be wrong, all subsequent operations cannot be correct. )
Step 2: implement the relationship between two tables in the database
Step 3: describe the relationship between two entities in entity class
Step 4: configure the relationship mapping between entity classes and database tables (emphasis)

4 one to many in JPA

4.1 example analysis

The examples we use are customers and contacts.
Customer: it refers to A company. We call it A.
Contact person: refers to the employees in company A.
Without considering part-time jobs, the relationship between the company and its employees is one to many.

4.2 table relation establishment

In a one to many relationship, we are used to call one party the primary table and the other the subordinate table. To establish a one to many relationship in a database, you need to use the foreign key constraint of the database.

What is foreign key?
                        .

Establish a one to many database relationship, as shown in the following figure:

/*Create customer table*/
CREATE TABLE cst_customer (
	cust_id BIGINT (32) NOT NULL AUTO_INCREMENT COMMENT 'Customer number(Primary key)',
	cust_name VARCHAR (32) NOT NULL COMMENT 'Customer name(corporate name)',
	cust_source VARCHAR (32) DEFAULT NULL COMMENT 'Customer information source',
	cust_industry VARCHAR (32) DEFAULT NULL COMMENT 'Customer industry',
	cust_level VARCHAR (32) DEFAULT NULL COMMENT 'Customer level',
	cust_address VARCHAR (128) DEFAULT NULL COMMENT 'Customer contact address',
	cust_phone VARCHAR (64) DEFAULT NULL COMMENT 'Customer contact number',
	PRIMARY KEY (`cust_id`)
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;

/*Create contact*/
CREATE TABLE `cst_linkman` (
	`lkm_id` BIGINT (32) NOT NULL AUTO_INCREMENT,
	`lkm_name` VARCHAR (32) DEFAULT NULL,
	`lkm_gender` VARCHAR (32) DEFAULT NULL,
	`lkm_phone` VARCHAR (32) DEFAULT NULL,
	`lkm_mobile` VARCHAR (32) DEFAULT NULL,
	`lkm_email` VARCHAR (32) DEFAULT NULL,
	`lkm_position` VARCHAR (32) DEFAULT NULL,
	`lkm_memo` VARCHAR (32) DEFAULT NULL,
	`lkm_cust_id` BIGINT (32) DEFAULT NULL,
	PRIMARY KEY (`lkm_id`),
	FOREIGN KEY (`lkm_cust_id`) REFERENCES cst_customer(`cust_id`) ON DELETE SET NULL
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;



4.3 entity class relationship establishment and mapping configuration

   in the entity class, because the customer is a small party, it should contain multiple contacts, so the entity class should reflect the information of multiple contacts in the customer, the code is as follows:

/** 
 * Entity class of customer 
 * All annotations used explicitly are JPA specifications 
 * Therefore, all the export packages must be imported into the javax.persistence package 
 * */ 
@Entity//Indicates that the current class is an entity class 
@Table(name="cst_customer")//Establish correspondence between current entity class and table 
public class Customer implements Serializable { 
    @Id//Indicates that the current private property is a primary key 
    @GeneratedValue(strategy=GenerationType.IDENTITY)//Specify the primary key generation policy 
    @Column(name="cust_id")//Specifies that it corresponds to cust "ID column in database table 
    private Long custId; 
    @Column(name="cust_name")//Specify to correspond to cust "name column in database table 
    private String custName; 
    @Column(name="cust_source")//Specify the corresponding cust source column in the database table 
    private String custSource; 
    @Column(name="cust_industry")//Specify the corresponding cust industry column in the database table 
    private String custIndustry; 
    @Column(name="cust_level")//Specifies the corresponding cust level column in the database table 
    private String custLevel; 
    @Column(name="cust_address")//Specifies that it corresponds to cust "address column in database table 
    private String custAddress; 
    @Column(name="cust_phone")//Specify the corresponding cust? Phone column in the database table 
    private String custPhone; //Configure one to many relationships between customers and contacts 
    
    @OneToMany(targetEntity=LinkMan.class) 
    @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id") 
    private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);
    
    public Long getCustId() { return custId; } 
    public void setCustId(Long custId) { this.custId = custId; } 
    public String getCustName() { return custName; } 
    public void setCustName(String custName) { this.custName = custName; } 
    public String getCustSource() { return custSource; } 
    public void setCustSource(String custSource) { this.custSource = custSource; } 
    public String getCustIndustry() { return custIndustry; } 
    public void setCustIndustry(String custIndustry) { this.custIndustry = custIndustry; } 
    public String getCustLevel() { return custLevel; } 
    public void setCustLevel(String custLevel) { this.custLevel = custLevel; } 
    public String getCustAddress() { return custAddress; }
    public void setCustAddress(String custAddress) { this.custAddress = custAddress; } 
    public String getCustPhone() { return custPhone; } 
    public void setCustPhone(String custPhone) { this.custPhone = custPhone; } 
    public Set<LinkMan> getLinkmans() { return linkmans;} 
    public void setLinkmans(Set<LinkMan> linkmans) { this.linkmans = linkmans; } 
    @Override 
    public String toString() { 
        return "Customer [custId=" + custId + ", custName=" + custName + ", custSource=" + custSource + ", custIndustry=" + custIndustry + ", custLevel=" + custLevel + ", custAddress=" + custAddress + ", custPhone=" + custPhone + "]"; 
    } 
}

Since the contact is one of many parties, it should be reflected in the entity class that each contact can only correspond to one customer. The code is as follows:

/** 
 * Entity class of contact (data model) 
 * */ 
@Entity 
@Table(name="cst_linkman") 
public class LinkMan implements Serializable { 
    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    @Column(name="lkm_id") 
    private Long lkmId; 
    @Column(name="lkm_name") 
    private String lkmName; 
    @Column(name="lkm_gender") 
    private String lkmGender; 
    @Column(name="lkm_phone") 
    private String lkmPhone; 
    @Column(name="lkm_mobile") 
    private String lkmMobile; 
    @Column(name="lkm_email") 
    private String lkmEmail; 
    @Column(name="lkm_position") 
    private String lkmPosition; 
    @Column(name="lkm_memo") 
    private String lkmMemo; 
    //Many to one relationship mapping: multiple contacts correspond to customers 
    @ManyToOne(targetEntity=Customer.class) 
    @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id") 
    private Customer customer;
    //Use its primary key to correspond to the foreign key in the contact table 
    public Long getLkmId() { return lkmId; } 
    public void setLkmId(Long lkmId) { this.lkmId = lkmId; } 
    public String getLkmName() { return lkmName; } 
    public void setLkmName(String lkmName) { this.lkmName = lkmName; } 
    public String getLkmGender() { return lkmGender; } 
    public void setLkmGender(String lkmGender) { this.lkmGender = lkmGender; } 
    public String getLkmPhone() { return lkmPhone; } 
    public void setLkmPhone(String lkmPhone) { this.lkmPhone = lkmPhone; } 
    public String getLkmMobile() { return lkmMobile; } 
    public void setLkmMobile(String lkmMobile) { this.lkmMobile = lkmMobile; } 
    public String getLkmEmail() { return lkmEmail; } 
    public void setLkmEmail(String lkmEmail) { this.lkmEmail = lkmEmail; } 
    public String getLkmPosition() { return lkmPosition; } 
    public void setLkmPosition(String lkmPosition) { this.lkmPosition = lkmPosition; } 
    public String getLkmMemo() { return lkmMemo;} 
    public void setLkmMemo(String lkmMemo) { this.lkmMemo = lkmMemo; } 
    public Customer getCustomer() { return customer; } 
    public void setCustomer(Customer customer) { this.customer = customer; } 
    @Override 
    public String toString() { 
        return "LinkMan [lkmId=" + lkmId + ", lkmName=" + lkmName + ", lkmGender=" + lkmGender + ", lkmPhone=" + lkmPhone + ", lkmMobile=" + lkmMobile + ", lkmEmail=" + lkmEmail + ", lkmPosition=" + lkmPosition + ", lkmMemo=" + lkmMemo + "]"; 
    } 
}

4.4 notes on mapping

4.4.1 @OneToMany:

effect:
Establish one to many relationship mapping
Properties:
   targetEntityClass: Specifies the bytecode of a multi-party class
   mappedBy: Specifies the name of the primary table object to be referenced from the table entity class.
   cascade: Specifies the cascading operation to use
                 
                  

4.4.2 @ManyToOne:

effect:
Establish many to one relationship
Properties:
   targetEntityClass: the bytecode of the specified one
   cascade: Specifies the cascading operation to use
                 
Optional: whether the association is optional. If set to false, a non empty relationship must always exist.

4.4.3 @JoinColumn

effect:
   used to define the correspondence between the primary key field and the foreign key field.
Properties:
   Name: Specifies the name of the foreign key field
   referencedColumnName: Specifies the name of the primary key field that references the primary table
Unique: unique or not. The default value is not unique
   nullable: whether it is allowed to be empty. The default value allows.
   insertable: whether to allow insertion. The default value allows.
    updatable: whether to allow updates. The default value allows.
   columnDefinition: the definition information of the column.

4.5 one to many operation

4.5.1 add

/** 
 * Save operation 
 * Requirements: 
 * Save a customer and a contact 
 * requirement: 
 * Create a customer object and a contact object 
 * Establish the relationship between customers and contacts (two-way one to many relationship) 
 * Save customer first, then contact 
 * */ 
 @Test 
 public void test1(){ 
     //Create customer and contact objects 
     Customer c = new Customer();
     //Instantaneous state 
     c.setCustName("TBD Cloud Center"); 
     c.setCustLevel("VIP Customer"); 
     c.setCustSource("network"); 
     c.setCustIndustry("Commercial Office"); 
     c.setCustAddress("Beiqijia Town, Changping District"); 
     c.setCustPhone("010-84389340"); 
     LinkMan l = new LinkMan();//Instantaneous state 
     l.setLkmName("TBD contacts"); 
     l.setLkmGender("male"); 
     l.setLkmMobile("13811111111"); 
     l.setLkmPhone("010-34785348"); 
     l.setLkmEmail("98354834@qq.com"); 
     l.setLkmPosition("Teacher"); 
     l.setLkmMemo("that's OK"); 
     //Establish their two-way one to many relationship 
     l.setCustomer(c); 
     c.getLinkmans().add(l); 
     EntityManager em = JPAUtil.createEntityManager(); 
     EntityTransaction tx = em.getTransaction(); 
     em.begin(); 
     //According to the requirements: save the customer first, then the contact person (in accordance with the saving principle: save the main table first, then the slave table) 
     em.persist(c);
     //If the customer object is converted to persistent state, the contact information is not considered. There will be no snapshot of contacts 
     em.persist(l); 
     tx.commit();
     //By default, the snapshot mechanism will be executed at this time. When the first level cache is found inconsistent with the snapshot, the database will be updated with the first level cache. 
} 

Through the saved case, we can find that after the bidirectional relationship is set, two insert statements and one redundant update statement will be sent

4.5.2 deletion

/** 
 * Delete operation 
 * Delete from table data: you can delete at any time. 
 * Delete main table data: 
 * Data reference from table 
 * 1,By default, it sets the foreign key field to null and then deletes the main table data. 
 * If there is a non empty constraint on the foreign key field in the table structure of the database, an error will be reported by default.
 * 2,If you have configured to give up the right to maintain the association relationship, you cannot delete it (no relationship with whether the foreign key field is allowed to be null) 
 * Because when it is deleted, it will not update the foreign key field from the table at all. 
 * 3,If you also want to delete, use cascading delete 
 * No reference from table data: delete at will 
 * In the actual development, please use it with caution! (in the case of one to many) 
 * */ 
@Test 
public void test3(){ 
    EntityManager em = JPAUtil.createEntityManager(); 
    EntityTransaction tx = em.getTransaction(); 
    em.begin(); 
    //Query customer with id 1 
    Customer c1 = em.find(Customer.class, 2L); 
    //Delete customer with id 1 
    em.remove(c1); 
    tx.commit(); 
}

Cascade operation: refers to the operation of an object and its associated objects
Usage: you only need to configure cascade on the annotation of the operation body

/** 
 * cascade:Configure cascading operations 
 * CascadeType.MERGE update cascade  
 * CascadeType.PERSIST Cascade save: 
 * CascadeType.REFRESH Cascade refresh: 
 * CascadeType.REMOVE Cascade delete: 
 * CascadeType.ALL Include all 
 * */ 
@OneToMany(mappedBy="customer",cascade=CascadeType.ALL,targetEntity=LinkMan.class) 
@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id") 
private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);

5 many to many in JPA

5.1 example analysis

The examples we take are users and roles.
User: it refers to every student in our class.
Role: refers to the identity information of our classmates.
For example, classmate A is my student. If one of his identities is A student or A child at home, then he has another identity as A child.
At the same time, classmate B also has the status of student and child.
Then any student may have multiple identities. At the same time, the identity of students can be possessed by multiple students.
So we say that the relationship between users and roles is many to many.

5.2 table relation establishment

The many to many table relationship is established by an intermediate table, in which the relationship between the user table and the intermediate table is one to many, and the relationship between the role table and the intermediate table is one to many, as shown in the following figure:

5.3 entity class relationship establishment and mapping configuration

A user can have multiple roles, so the user entity class should contain information about multiple roles. The code is as follows:

/** 
 * User's data model 
 * */ 
@Entity 
@Table(name="sys_user") 
public class SysUser implements Serializable {
    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    @Column(name="user_id") 
    private Long userId; 
    @Column(name="user_code") 
    private String userCode; 
    @Column(name="user_name") 
    private String userName; 
    @Column(name="user_password") 
    private String userPassword; 
    @Column(name="user_state") 
    private String userState; 
    //Many to many relationship mapping 
    @ManyToMany(mappedBy="users") 
    private Set<SysRole> roles = new HashSet<SysRole>(0); 

    public Long getUserId() { return userId; } 
    public void setUserId(Long userId) { this.userId = userId; } 
    public String getUserCode() { return userCode; } 
    public void setUserCode(String userCode) { this.userCode = userCode; } 
    public String getUserName() { return userName; } 
    public void setUserName(String userName) { this.userName = userName; } 
    public String getUserPassword() { return userPassword; } 
    public void setUserPassword(String userPassword) { this.userPassword = userPassword; } 
    public String getUserState() { return userState; }
    public void setUserState(String userState) { this.userState = userState; } 
    public Set<SysRole> getRoles() { return roles; } 
    public void setRoles(Set<SysRole> roles) { this.roles = roles; } 
    
    @Override 
    public String toString() { 
        return "SysUser [userId=" + userId + ", userCode=" + userCode + ", userName=" + userName + ", userPassword=" + userPassword + ", userState=" + userState + "]"; 
    } 
}

A role can be assigned to multiple users, so the role entity class should contain the information of multiple users. The code is as follows:

/** 
 * Data model for roles 
 * */ 
@Entity 
@Table(name="sys_role") 
public class SysRole implements Serializable { 
    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    @Column(name="role_id") 
    private Long roleId; 
    @Column(name="role_name") 
    private String roleName; 
    @Column(name="role_memo") 
    private String roleMemo; 
    //Many to many relationship mapping 
    @ManyToMany 
    @JoinTable(name="user_role_rel", //Middle table name 
    //The user role rel field in the middle table is associated with the role ID of the primary key field in the sys role table 
    joinColumns={@JoinColumn(name="role_id",referencedColumnName="role_id")}, 
    //The field of the intermediate table user role rel is associated with the primary key user ID of the sys user table 
    inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")} )
    private Set<SysUser> users = new HashSet<SysUser>(0); 

    public Long getRoleId() { return roleId; } 
    public void setRoleId(Long roleId) { this.roleId = roleId; } 
    public String getRoleName() { return roleName; } 
    public void setRoleName(String roleName) { this.roleName = roleName; } 
    public String getRoleMemo() { return roleMemo; } 
    public void setRoleMemo(String roleMemo) { this.roleMemo = roleMemo; } 
    public Set<SysUser> getUsers() { return users; } 
    public void setUsers(Set<SysUser> users) { this.users = users; } 

    @Override 
    public String toString() { 
        return "SysRole [roleId=" + roleId + ", roleName=" + roleName + ", roleMemo=" + roleMemo + "]"; 
    } 
}

5.4 notes on mapping

5.4.1 @ManyToMany

effect:
   for mapping many to many relationships
Properties:
cascade: configure cascading operations.
                 .
   targetEntity: configure the entity class of the target. Do not write when mapping many to many.

5.4.2 @JoinTable

effect:
   configuration for intermediate tables
Properties:
       name: the name of the configuration intermediate table
   joinColumns: the foreign key field of the intermediate table is associated with the primary key field of the table corresponding to the current entity class
   inverseJoinColumn: the foreign key field of the intermediate table is associated with the primary key field of the opposite table

5.4.3 @JoinColumn

effect:
   used to define the correspondence between the primary key field and the foreign key field.
Properties:
   Name: Specifies the name of the foreign key field
   referencedColumnName: Specifies the name of the primary key field that references the primary table
Unique: unique or not. The default value is not unique
   nullable: whether it is allowed to be empty. The default value allows.
   insertable: whether to allow insertion. The default value allows.
    updatable: whether to allow updates. The default value allows.
   columnDefinition: the definition information of the column.

5.5 many to many operation

5.5.1 preservation

/** 
 * Requirements: 
 * Save users and roles 
 * requirement: 
 * Create 2 users and 3 roles 
 * Let user 1 have roles 1 and 2 (bidirectional) 
 * Let user 2 have roles 2 and 3 (bidirectional) 
 * Save users and roles 
 * Question: 
 * When saving, there will be a duplicate primary key error, because it is caused by saving data to the intermediate table. 
 * terms of settlement: 
 * Let either party waive the right to maintain the relationship
 * */ 
@Test 
public void test1(){ 
    //create object 
    SysUser u1 = new SysUser(); 
    u1.setUserName("User 1"); 
    SysUser u2 = new SysUser(); 
    u2.setUserName("User 2"); 
    SysRole r1 = new SysRole(); 
    r1.setRoleName("Role 1"); 
    SysRole r2 = new SysRole(); 
    r2.setRoleName("Role 2"); 
    SysRole r3 = new SysRole(); 
    r3.setRoleName("Role 3"); 
    //Building relationships 
    u1.getRoles().add(r1); 
    u1.getRoles().add(r2); 
    r1.getUsers().add(u1); 
    r2.getUsers().add(u1); 
    u2.getRoles().add(r2); 
    u2.getRoles().add(r3); 
    r2.getUsers().add(u2); 
    r3.getUsers().add(u2); 
    EntityManager em = JPAUtil.createEntityManager(); 
    EntityTransaction tx = em.getTransaction(); 
    em.begin(); 
    em.persist(u1); 
    em.persist(u2); 
    em.persist(r1); 
    em.persist(r2); 
    em.persist(r3); 
    tx.commit(); 
}

5.5.2 deletion

/** 
 * Delete operation 
 * Delete from table data: you can delete at any time. 
 * Delete main table data: 
 * Data reference from table 
 * 1,Cannot delete 
 * 2,If you also want to delete, use cascading delete 
 * No reference from table data: delete at will 
 * In the actual development, please use it with caution! (in the case of one to many) 
 * */ 
@Test 
public void test3(){ 
    //Get JPA operation comparison 
    EntityManager em = JPAUtil.getEntityManager(); 
    //Get JPA transaction object 
    EntityTransaction tx= em.getTransaction(); 
    //Open transaction 
    tx.begin(); 
    //Query customer with id 1 
    Customer c1 = em.find(Customer.class, 2L); 
    //Delete customer with id 1 
    em.remove(c1); 
    tx.commit(); 
} 

Configuration for cascade deletion:

@OneToMany(targetEntity=LinkMan.class ,cascade=CascadeType.ALL) //You can also use CascadeType.REMOVE 
private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);

6 query in JPA

6.1 object navigation query (multi table query is very convenient)

6.1.1 general

The object graph navigation retrieval method is to navigate to its associated objects according to the loaded objects. It uses the relationship between classes to retrieve objects.

For example, if we find a Customer through ID query, we can call getLinkMans() method in the Customer class to get all contacts of the Customer.

The usage requirement of object navigation query is that there must be an association relationship between two objects. \The use of color{red} {object navigation query requires that there must be an association between two objects. }The usage requirement of object navigation query is that there must be an association relationship between two objects.

6.1.2 example of object navigation and retrieval

Query a customer and get all contacts under the customer:

/** 
 * Requirements: 
 * How many contacts are there for 1 customer with ID 
 * */ 
@Test 
public void test1(){ 
    //Get JPA operation comparison 
    EntityManager em = JPAUtil.getEntityManager(); 
    //Get JPA transaction object 
    EntityTransaction tx= em.getTransaction(); 
    //Open transaction 
    tx.begin(); 
    Customer c = em.find(Customer.class, 1L); 
    Set<LinkMan> linkmans = c.getLinkmans();
    //Here is the object navigation query 
    for(Object o : linkmans){ 
        System.out.println(o); 
    } 
    tx.commit(); 
}

Query a contact to get all customers of the contact:


/** 
 * Requirements: 
 * Query the customer of the contact with ID 1 
 * */ 
@Test 
public void test3(){ 
    //Get JPA operation comparison 
    EntityManager em = JPAUtil.getEntityManager(); 
    //Get JPA transaction object 
    EntityTransaction tx= em.getTransaction(); 
    //Open transaction 
    tx.begin(); 
    LinkMan l = em.find(LinkMan.class, 1L); 
    System.out.println(l.getCustomer()); 
    tx.commit(); 
}

6.1.3 problem analysis of object navigation query

Question 1: do we want to find out the contact person when we query the customer?

analysis:
If we don't check, we need to write our own code and call methods to query.
If we find out, we will waste the server memory when we don't use it.

solve:
The idea of delay loading is adopted. It is configured to initiate a real query when we need to use it.

How to configure:

/** 
 * Add the fetch attribute to the @ OneToMany annotation of the customer object 
 * FetchType.EAGER : Load now 
 * FetchType.LAZY : Delay loading 
 * */ 
@OneToMany(mappedBy="customer",fetch=FetchType.LAZY) 
private Set<LinkMan> linkMans = new HashSet<>(0)

Question 2: when we check the contact person, do we want to find out the customer?

analysis:
If we don't check, we need to write our own code and call methods to query.
If we find out, an object will not consume too much memory. And most of the time we use it.
For example: when querying the contact details, you will definitely see the customer to which the contact belongs.

solve:
Adopt the idea of immediate loading. It is set by configuration. As long as the slave table entity is queried, the primary table entity object is queried at the same time.

How to configure:


/** 
 * Add the fetch attribute to the @ ManyToOne annotation of the contact object 
 * FetchType.EAGER : Load now 
 * FetchType.LAZY : Delay loading 
 * */
@ManyToOne(targetEntity=Customer.class,fetch=FetchType.EAGER) 
@JoinColumn(name="cst_lkm_id",referencedColumnName="cust_id") 
private Customer customer;

6.2 summary of various query methods in JPA

As the name implies: query an entity according to the primary key. Two methods are provided in JPA. namely:

find(Class entityClass,Object id);
getReference(Class entityClass,Object id);

Their differences are:

The timing of the query is different:
find's method is to load immediately, as soon as the method is called, query will be launched immediately.
The getReference method is delayed loading, and queries are initiated only when the data is actually used. (load on demand)

The results returned are different:
The find method returns an entity class object.
The getReference method returns the proxy object of the entity class.

Example:

//Query a 
//Load now 
@Test 
public void testFindOne() { 
    EntityManager em = JPAUtil.createEntityManager(); 
    EntityTransaction tx = em.getTransaction(); 
    tx.begin(); 
    Customer c = em.find(Customer.class, 1); 
    System.out.println(c); 
    tx.commit(); 
    em.close(); 
} 
//Query a 
//Lazy load (delayed load) 
@Test 
public void testFindOne2() { 
    EntityManager em = JPAUtil.createEntityManager(); 
    EntityTransaction tx = em.getTransaction(); 
    tx.begin(); 
    Customer c = em.getReference(Customer.class, 1); 
    System.out.println(c.toString()); 
    tx.commit(); 
    em.close(); 
}

6.2.3 JPQL query

   this way is to query using JPQL statements. The full name is Java Persistence Query Language. JPQL statement is a query language defined in JPA. The purpose of this language is to let developers ignore the fields in database tables and tables, and pay attention to the properties in entity classes and entity classes. A more suitable operation entity class is equivalent to the ORM idea of operating database tables. But it is not completely separated from the SQL statement, for example:

Sort, still using the order keyword.
Aggregation function: it can also be used in JPQL.

It is written as follows:
                        .

Note:
                        . Instead of private class member variables. But our get/set methods are all generated by tools, so we can write the private member variable name directly.

Example:

@Test 
public void testFindAll() { 
    EntityManager em = JPAUtil.createEntityManager(); 
    EntityTransaction tx = em.getTransaction(); 
    tx.begin(); 
    //The writing method of JPQL is to change the column name into attribute name and the table name into entity class name in SQL statement 
    //select * from cst_customer 
    Query query = em.createQuery("select c from Customer c ");
    //This is a JPQL statement java persistence query language 
    List<Customer> customers = query.getResultList(); 
    for(Customer c : customers) { 
        System.out.println(c); 
    } 
    tx.commit(); 
    em.close(); 
}

6.2.4 SQL query

   this way is to use the native SQL statement to query the database. With this way of query, we can write the statement first in the database visual compiler, and then paste it into the code.

Note:
                      . (except in certain cases, such as statements for statistical analysis)

Example:

@Test 
public void test3() { 
    EntityManager em = JPAUtil.createEntityManager(); 
    EntityTransaction tx = em.getTransaction(); 
    tx.begin(); 
    // The first parameter in the createNativeQuery method is the SQL statement, and the second parameter is the entity class bytecode to be encapsulated 
    Query query = em.createNativeQuery("select * from cst_customer",Customer.class); 
    List list = query.getResultList(); 
    System.out.println(list); 
}

6.2.5 QBC query

The full name of QBC is Query By Criteria. This is a more object-oriented query method. It does not need to consider how to implement the underlying database and how to write SQL statements.

Details:
If JPQL can check, QBC can check, and vice versa.

Example:

@Test 
public void test1() { 
    //1. Get the operation database object of JPA 
    EntityManager em = JPAUtil.createEntityManager(); 
    //2. Get transaction object 
    EntityTransaction tx = em.getTransaction(); 
    //3. Open transaction 
    tx.begin(); 
    //4. Create the CriteriaBuilder object, which defines the methods involved in the query conditions 
    CriteriaBuilder cb = em.getCriteriaBuilder(); 
    //5. Get the query object CriteriaQuery of QBC 
    //It can add some SQL clauses, such as where group by order by having and so on. 
    CriteriaQuery<Customer> cq = cb.createQuery(Customer.class); 
    //6. Get the encapsulation object of the entity class object. With this object, all entity classes can be regarded as this type 
    Root<Customer> root = cq.from(Customer.class); 
    //7. Create condition object 
    Predicate p1 = cb.like(root.get("custName"), "%repair%"); 
    Predicate p2 = cb.equal(root.get("custLevel"), "22"); 
    //8. Set query conditions for query objects 
    cq.where(p1,p2); 
    //9. Execute the query to get the result 
    List list = em.createQuery(cq).getResultList(); 
    System.out.println(list); 
    //10. Release 
    tx.commit(); 
    em.close(); 
}

Tags: Database snapshot Hibernate SQL

Posted on Fri, 08 May 2020 06:01:37 -0400 by tarscher