Having covered the CRUD operations of SpringDataJpa and the analysis of its underlying proxy implementation in the previous sections, the following sections introduce complex and dynamic queries, multi-table queries in SpringDataJpa. (Nanny Level Tutorial)

The article is more words, please read according to your needs.

If you are not familiar with JPA, please refer to this article: Introduction to JPA;

If you are not sure about SpringDataJPA environment, please refer to this article: SpringDataJPA Introduction case;

To learn more about the implementation process of the SpringDataJPA proxy class, see the article: Underlying Implementation Principles of SpringDadaJPA

If need to reprint, please indicate the source.

1. Complex query

I. Query method name rules

Method name query: just define methods in the dao interface according to the method name rules provided by SpringDataJpa.

There is a convention for method names.

KeyWord Sample JPQL
And findByLastnameAndFirstname where x.lastname = ? 1 and x.firstname = ? 2
Or findByLastnameOrFirstname where x.lastname = ? 1 or x.firstname = ? 2
Between findByAgeBetween where x.Age between ? 1 and ? 2
LessThan findByAgeLessThan where x.age < ? 1
GreaterThan findByAgeGreaterThan where x.age > ? 1
Like findByFirstnameLike where x.firstname like ? 1
NotLike findByFirstnameNotLike where x.firstname not like ? 1
TRUE findByActiveTrue() where x.active = true
FALSE findByActiveFalse() where x.active = false
public interface CustomerDao extends JpaRepository<Customer.Long>, JpaSpecificationExecutor<Customer> {
    FindBy: findBy: findBy: findBy: findBy: findBy: findBy: findBy: findBy: findBy: findBy: findBy: findBy * Special query method, For example, the fuzzy query * findByCustName----- query findBy based on the customer name indicates the CustName attribute name to be queried. * springDataJpa will resolve findBy from XXX(entity class) * based on the method name at run time Attribute names where custName * 1. FindBy + attribute name (matching task according to the attribute name) * 2. FindBy + attribute name + query mode (Like | isnull) * 3. Many conditions findBy query * + attribute name + query mode + conditions more connector (and | or) + attribute name + * / query mode
    public List<Customer> findByCustName(String name);
    // Query the user whose id is 3 and whose name contains the university
    public Customer findByCustId(Long id);
    public Customer findByCustIdAndCustNameLike(Long id,String name);
}

Copy the code

Ii. The JPQL query

The Query method provided by Spring Data JPA can already solve most application scenarios, but for some businesses, we still need to construct the Query conditions flexibly. In this case, we can use the @Query annotation, combined with the JPQL statement to complete the Query.

Using the @Query annotation is as simple as annotating the annotation above the method and providing a JPQL Query statement

Note:

To perform an update operation using @Query, we need to use @Query while labeling the operation as a Modifying Query with @modifying, so that the framework ends up producing an update operation rather than a Query.

public interface CustomerDao extends JpaRepository<Customer.Long>, JpaSpecificationExecutor<Customer> {
    * JPQL :from Customer where custName=? * /
    @Query(value="from Customer where custName =?")
    public List<Customer> findCustomerJpql(String name);
    /** * 2. Query by customer name and customer ID * for multiple placeholder arguments * By default, the position of the placeholder argument needs to be the same as the position in the method argument * you can also specify the position of the placeholder argument (note: no Spaces in the middle) *? To specify the source of the value of this placeholder eg? 2 indicates that this placeholder corresponds to the second argument */
    @Query(value="from Customer where custName=? 2 and custId=? 1")
    public Customer findByNameAndId(Long id,String name);
    SQL :update CST_customer set CUST_name =? where cust_id=? * JPQL: Update Customer set custName=? where custId=? * *@query: indicates that the method is used to perform an update operation@Modifying* /
    @Query(value = "update Customer set custName=? where custId=?")
    @Modifying
    public void updateCustomerName(String name,Long id);
}
Copy the code

Note: in executing springDataJpa with JPQL to complete the update, delete the operation, need to manually add transaction support is required; By default, the transaction is rolled back after execution.

 @Test
    @Transactional// Add transaction support
    @Rollback(value = false)
    public void updateCustomerName(a){
        customerDao.updateCustomerName("Student Housing".4L);
    }
Copy the code

Iii. The SQL query

Spring Data JPA also supports SQL statement queries as follows:

SQL :select * from cst_customer * nativeQuery = true The return value is a list */ of type Object[]
// @Query(value = "select * from cst_customer",nativeQuery = true)
    // public ListfindSql();
    @Query(value = "select * from cst_customer where cust_name like ?",nativeQuery = true)
    public List<Object []>findSql(String name);
Copy the code

2. Dynamic query

Interface specification for SpringDatajPA:

  • JpaRepository< the entity type of the operation, the type of the primary key attribute in the entity type >

    Encapsulates basic CRUD operations, paging, etc.

  • JpaSpecificationExecutor< Entity class type for the operation >

    Encapsulates complex queries.

The above query method uses methods in the JpaRepository interface. Let’s examine methods in the JpaSpecificationExecutor.

I. Why are dynamic queries needed

Why do you need dynamic queries? Sometimes when we are querying an entity, the given query conditions are not fixed, so we need to build the corresponding query statement dynamically. It can be understood that the above query conditions are defined in the DAO interface, and the dynamic query conditions are defined in the implementation class.

Ii. JpaSpecificationExecutor defined in the method

public interface JpaSpecificationExecutor<T> {
    T findOne(Specification<T> var1);

    List<T> findAll(Specification<T> var1);

    Page<T> findAll(Specification<T> var1, Pageable var2);

    List<T> findAll(Specification<T> var1, Sort var2);

    long count(Specification<T> var1);
}
Copy the code

In the above approach, we can see the interface Specification. Specification constructs query conditions. Let’s look at the methods defined in Specification.

/* * root: T indicates the type of the query object and represents the root object of the query. Attributes in the entity can be obtained through root * query: represents a top-level query object and uses custom query * cb: used to build the query
public interface Specification<T> {
    Predicate toPredicate(Root
       
         root, CriteriaQuery
         query, CriteriaBuilder cb)
       ;
}
Copy the code

Unlike the above query approach, complex queries are defined in the DAO interface, while dynamic queries are defined in the implementation class.

1) Single condition query

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpecTest {
    @Autowired
    private CustomerDao customerDao;
    @Test
    public void conditionTest(a){
        Specification interface (provide generics: query object type, write that object type) * 2. Implement the toPredicate method (construct query criteria) * 3. Require two of the library method parameters * root: object attributes used to retrieve queries * CriteriaBuilder: construct query criteria, encapsulating a number of query criteria internally (e.g., fuzzy matching, exact matching) * Requirements: Query by customer name, query customer name is university * query condition * 1. Query method (accurate matching, whether it is empty...) * CriteriaBuilder object * 2. Attribute names for comparison (with which field to compare and how) * root object */

        Specification<Customer> spec=new Specification<Customer>() {
            @Override
             public Predicate toPredicate(Root
       
         root, CriteriaQuery
         criteriaQuery, CriteriaBuilder cb)
        {
                //1. Get the comparison attribute (not the field name)
                Path<Object> custName = root.get("custName");
                //2. Construct the query condition
                /** * First argument: the attribute to compare (Path) * second argument: the value of the current comparison */
                Predicate predicate = cb.equal(custName, "Three Gorges University");// Perform exact matching (compare attributes, compare attribute values)
                returnpredicate; }};// Select findOne or findAll based on the number of returned objectsCustomer customer = customerDao.findOne(spec); System.out.println(customer); }}Copy the code

2) Multi-condition query

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpecTest {
    @Autowired
    private CustomerDao customerDao;
/** * Multi-condition query: query by user name and industry * root: obtain attributes * user name * industry * cb: construct query * 1. Construct accurate matching query of customer name * 2. Construct accurate matching query of industry * 3, and connect the above two queries */
    @Test
    public void findByNmaeAndIndustray(a){
        Specification<Customer> spec=new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root
       
         root, CriteriaQuery
         criteriaQuery, CriteriaBuilder cb)
        {
                //1. Get attributes
                Path<Object> custName = root.get("custName");
                Path<Object> industry = root.get("custIndustry");
                //2. Construct query
                Predicate p1 = cb.equal(custName, "6 Test data - Coderxz");
                Predicate p2 = cb.equal(industry, "6 Test Data - Java Engineer");
                / / 3. Group multiple query criteria together (and/ OR)
                Predicate predicate = cb.and(p1, p2);
                returnpredicate; }}; Customer customer = customerDao.findOne(spec); System.out.println(customer); }}Copy the code

3) Fuzzy query

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpecTest {
    @Autowired
    private CustomerDao customerDao;
    ** * for gt,lt,le,like: The path object specifies the type of the comparison argument (string or number...). * Specifies the parameter type path.as(bytecode object of type) */
    @Test
    public void findVagueCustomer(a){
        Specification<Customer>spec=new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root
       
         root, CriteriaQuery
         criteriaQuery, CriteriaBuilder criteriaBuilder)
        {
                Path<Object> custName = root.get("custName");
                Predicate predicate = criteriaBuilder.like(custName.as(String.class), University of "% %");
                returnpredicate; }}; List<Customer> customers = customerDao.findAll(spec);for(Customer c:customers){ System.out.println(c); }}}Copy the code

4) Paging query

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpecTest {
    @Autowired
    private CustomerDao customerDao;
* findAll(Specification,Pageable); * Specification; * Pageable; The condition * per page query returns: Pahe (StringDataJpa) for our encapsulated pageBean object, data list, */
    @Test
    public void pageCustomer(a){
        Specification<Customer> spec=new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root
       
         root, CriteriaQuery
         criteriaQuery, CriteriaBuilder criteriaBuilder)
        {
                return null; }};/** * PageRequest; /** * PageRequest; /** * PageRequest; Number of queries per page * Note: this method is obsolete in the new version of JPA; the new method is pagerequest.of (page,size) */
        Pageable pageable = new PageRequest(0.1);
        // Paging query Page is a JavaBean wrapped by SpringDataJpa for us
        Page<Customer> page = customerDao.findAll(spec, pageable);
        // Get the total number of pages (this data should be divided into several pages)
        System.out.println("Total page query:"+page.getTotalPages());
        // Get total number of records (total number of records in database)
        System.out.println("Total records queried:"+page.getTotalElements());
        // Get the list of data sets
        System.out.println("List of data sets :"+page.getContent()); }}Copy the code

5) Sort the query results

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpecTest {
    @Autowired
    private CustomerDao customerDao;
    /** * Sort the query results */
    @Test
    public void findSortCustomer(a){
        Specification<Customer>spec=new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root
       
         root, CriteriaQuery
         criteriaQuery, CriteriaBuilder criteriaBuilder)
        {
                Path<Object> custName = root.get("custName");
                Predicate predicate = criteriaBuilder.like(custName.as(String.class), University of "% %");
                returnpredicate; }};Sort.direction. DESC: sort.direction. ASC: sort.direction. ASC: sort.direction. ASC: sort.direction. ASC: sort.direction. ASC: sort.direction. ASC: sort.direction. ASC: sort.direction. ASC: sort.direction. ASC: sort.direction. ASC: sort.direction. ASC: sort.direction. ASC: sort.direction. ASC: sort.direction. ASC: sort.direction
        Sort sort = new Sort(Sort.Direction.DESC, "custId");
        List<Customer> customers = customerDao.findAll(spec,sort);
        for(Customer c:customers){ System.out.println(c); }}}Copy the code

3. Query multiple tables

Both the complex and dynamic queries described above are based on single-table queries, requiring only a one-to-one mapping between the entity class and the database table. Multi-table queries require modification of mappings between entity classes.

There are three types of relationships between tables in a database: many-to-many, one-to-many, and one-to-one.

Then the corresponding entity map should also have three relationships. So how do you analyze the relationship of tables in JPA?

1. Establish relationships between tables

  • Step 1: First determine the relationship between the two tables. If the relationship is determined incorrectly, nothing that follows can be done correctly.
  • Step 2: Implement the relationship between the two tables in the database
  • Step 3: Describe the relationship between the two entities in the entity class
  • Step 4: Configure the relational mapping between entity classes and database tables (emphasis)

4. One-to-many in JPA

Case Study:

Take two entity objects: the company and the employee

In the absence of part-time jobs, each employee corresponds to one company, and each company has multiple employees.

In one-to-many relationships, we are used to calling the one party the master table and the many party the slave table. Establishing a one-to-many relationship in a database requires the use of the database’s foreign key constraints.

** What are foreign keys? ** refers to a column from the table. The value of this column is a reference to the primary key in the primary table.

Database table:

CREATE TABLE `cst_customer` (
  `cust_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `cust_address` varchar(255) DEFAULT NULL,
  `cust_industry` varchar(255) DEFAULT NULL,
  `cust_level` varchar(255) DEFAULT NULL,
  `cust_name` varchar(255) DEFAULT NULL,
  `cust_phone` varchar(255) DEFAULT NULL,
  `cust_source` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8;

CREATE TABLE `cst_linkman` (
  `lkm_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `lkm_email` varchar(255) DEFAULT NULL,
  `lkm_gender` varchar(255) DEFAULT NULL,
  `lkm_memo` varchar(255) DEFAULT NULL,
  `lkm_mobile` varchar(255) DEFAULT NULL,
  `lkm_name` varchar(255) DEFAULT NULL,
  `lkm_phone` varchar(255) DEFAULT NULL,
  `lkm_position` varchar(255) DEFAULT NULL,
  `lkm_cust_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`lkm_id`),
  KEY `FKh9yp1nql5227xxcopuxqx2e7q` (`lkm_cust_id`),
  CONSTRAINT `FKh9yp1nql5227xxcopuxqx2e7q` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
Copy the code

1. Establish the mapping between entities and tables

Note: the annotations used are JPA compliant, the guide package needs to import the javac.persistence package

package ctgu.pojo;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

* @entity * @table (name=" cST_customer ")name specifies the name of the Table in the database * 2. Mapping between attributes in an entity class and fields in a table * @ID Setting for declaring a primary key * @GeneratedValue Configuring a primary key is a generation policy (automatic growth) * strategy= * generationType.identity: auto-increment Mysql * generationType. SEQUENCE: Oracle (the underlying database must support sequences) * generationType. TABLE: Jpa provides a mechanism to help us increment * generationType. AUTO in the form of a database table: the program automatically helps us select the primary key generation policy * @column (name = "CUST_ID ") the name of the field in the table in the database */
@Entity
@Table(name = "cst_customer")
public class Customer {
    /** * @id declare primary key setting * @GeneratedValue Configure primary key is generate policy (automatic growth) * generationType. IDENTITY * @column (name = "CUST_ID ") name of the field in the table in the database */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cust_id")
    private Long custId;
    @Column(name = "cust_name")
    private String custName;
    @Column(name = "cust_source")
    private String custSource;
    @Column(name = "cust_industry")
    private String custIndustry;
    @Column(name = "cust_level")
    private String custLevel;
    @Column(name = "cust_address")
    private String custAddress;
    @Column(name = "cust_phone")
    private String custPhone;
    /** * Configure the relationship between a customer and a contact (one customer corresponds to multiple contacts) * configure multiple table relationships with annotations * 1 Declare relationships * @onetomany: configure one-to-many relationships * targetEntity: bytecode object of the peer object * 2 Configure the foreign key (intermediate table) * @joinColumn * name: the field name of the secondary table of the foreign key (not an attribute, but a database field name) * referencedColumnName: the field name of the primary table referenced */
    @OneToMany(targetEntity = LinkMan.class)
    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
    private Set<LinkMan> linkMans=new HashSet<>();
    /* get/set/toString() is omitted...... * /
}
Copy the code
package ctgu.pojo;
import javax.persistence.*;
@Entity
@Table(name="cst_linkman")
public class LinkMan {
    @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;
    /** * Configure the many-to-one relationship between contacts and customers * the foreign key field is set in the slave table and is not configured as an object property, but as a foreign key. ** Configure the many-to-one relationship * 1 with annotations. Configure table relationships *@ManyToOne: Configures many-to-one relationship * targetEntity: peer entity class bytecode * 2. Configuring a foreign key (intermediate table) * * * The process of configuring a foreign key. If the foreign key is configured on more parties, it will be maintained on more parties
    @ManyToOne(targetEntity = Customer.class,fetch = FetchType.LAZY)
    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
    private Customer customer;
	/ * get/set/toString slightly... * /
}
Copy the code

Note: In all of the above entities, foreign keys are maintained.

2. Mapping notes

i.@OneToMany

Create a one-to-many relational mapping attribute:

  • TargetEntityClass: specifies the bytecode of a multi-party class (often used)
  • MappedBy: Specifies the name of the main table object referenced from the table entity class. (common)
  • Cascade: Indicates the cascade operation to be used
  • Fetch: Specifies whether lazy loading is used
  • OrphanRemoval: Whether an orphan deletion is used

ii.@ManyToOne

Function: Establish many-to-one relationship attributes:

  • TargetEntityClass: specifies a party entity class bytecode (often used)
  • Cascade: Indicates the cascade operation to be used
  • Fetch: Specifies whether lazy loading is used
  • Optional: Indicates whether the association is optional. If set to false, a non-null relationship must always exist.

iii.@JoinColumn

Purpose: Defines the mapping between a primary key field and a foreign key field. Properties:

  • Name: Specifies the name of the foreign key field (common)
  • ReferencedColumnName: specifies the name of the primary key field referencing the primary table (commonly used)
  • It’s unique. Default values are not unique
  • Nullable: Specifies whether to allow null. Default value Yes.
  • Insertable: Indicates whether the insertable is allowed. Default value Yes.
  • Updatable: Indicates whether updates are allowed. Default value Yes.
  • ColumnDefinition: specifies the columnDefinition.

3. One-to-many testing

I. Save the company and contact

package ctgu.OntoMany;

import ctgu.dao.CustomerDao;
import ctgu.dao.LinkManDao;
import ctgu.pojo.Customer;
import ctgu.pojo.LinkMan;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class OntoManyTest {
    @Autowired
    private CustomerDao customerDao;
    @Autowired
    private LinkManDao linkManDao;
    /** * Save a customer, save a contact * Symptom: The foreign key of the secondary table (contact) is empty * Cause: * No relationship is configured in the primary table */
    @Test
    @Transactional
    @Rollback(value = false)
    public void addTest(a){
        Customer customer = new Customer();
        LinkMan linkMan = new LinkMan();
        customer.setCustName("TBD Gathering Center");
        customer.setCustLevel("VIP customers");
        customer.setCustSource("Network");
        customer.setCustIndustry("Business office");
        customer.setCustAddress("Beiqijia Town, Changping District");
        customer.setCustPhone("010-84389340");
        
        linkMan.setLkmName("Xiao Ming");
        linkMan.setLkmGender("male");
        linkMan.setLkmMobile("13811111111");
        linkMan.setLkmPhone("010-34785348");
        linkMan.setLkmEmail("[email protected]");
        linkMan.setLkmPosition("Teacher");
        linkMan.setLkmMemo("Okay?");
        /** * The customer to contact relationship is configured * from the customer's point of view, two INSERT statements are sent, and one update statement is sent to update the database (update foreign key values from the table) * Since we configured the customer to contact relationship, the customer can maintain foreign keys */
        
        linkMan.setCustomer(customer);
        // This can be added without writing a meetingcustomer.getLinkMans().add(linkMan); customerDao.save(customer); linkManDao.save(linkMan); }}Copy the code

Running results:

Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into cst_linkman (lkm_cust_id, lkm_email, lkm_gender, lkm_memo, lkm_mobile, lkm_name, lkm_phone, lkm_position) values (?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: update cst_linkman set lkm_cust_id=? where lkm_id=?
Copy the code

Analysis:

Two INSERT statements and an UPDATE statement were executed, and one of the UPDATE statements was redundant. The reason for this is that we maintain the foreign key twice in both entity classes, and the solution is to give up one of the rights.

Modify: Change the relational mapping in the main table to:

 @OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
 private Set<LinkMan> linkMans=new HashSet<>();
Copy the code

Ii. Cascading add

Cascading operations: Operations on an object simultaneously operate on its associated objects

How to use it: Just configure casade on the annotations of the action body

 /** * waive foreign key maintenance: my one-to-many mapping refers to each other's attributes * mappedBy: Attribute name of the relationship maintained by the peer * CASCADE = CascadeType.ALL Indicates the cascading operation.ALL indicates ALL (INSERT, delete, Update) *.merge Update *. Persist Save *. Remove Remove * fetch configure lazy loading */
    @OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
    private Set<LinkMan> linkMans=new HashSet<>() 
Copy the code

Note: Use cascadetype. ALL with caution

 package ctgu.OntoMany;

import ctgu.dao.CustomerDao;
import ctgu.dao.LinkManDao;
import ctgu.pojo.Customer;
import ctgu.pojo.LinkMan;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class OntoManyTest {
    @Autowired
    private CustomerDao customerDao;
    @Autowired
    private LinkManDao linkManDao;
 /** * Cascade add: * Save a customer at the same time, save all the contacts of the customer * need to configure the casache property on the entity class of the action topic */
    @Test
    @Transactional
    @Rollback(value = false)
    public void cascadeAdd(a){
        Customer customer = new Customer();
        LinkMan linkMan = new LinkMan();
        customer.setCustName("Test Company 1");
        linkMan.setLkmName("Test Employee Zhang SAN 1");
        // Note the addition herelinkMan.setCustomer(customer); customer.getLinkMans().add(linkMan); customerDao.save(customer); }}Copy the code

Test results:

Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into cst_linkman (lkm_cust_id, lkm_email, lkm_gender, lkm_memo, lkm_mobile, lkm_name, lkm_phone, lkm_position) values (?, ?, ?, ?, ?, ?, ?, ?)
Copy the code

Iii. Cascading deletion

When deleting a company, delete all employees of the corresponding company.

A delete in JPA is a query followed by a delete.

 /** * Cascading delete: Delete all contacts * 1 of customer 1 when deleting customer 1. Need to distinguish the action subject (which object you are operating on) * 2. Need to add cascading properties to the entity class of the action subject (need to add annotations to multi-table mappings) * 3. Cascade (configure cascading) */
    @Test
    @Transactional
    @Rollback(value = false)
    public void cascadeDelete(a){
    
// Customer customer = customerDao.findOne(1L);
        customerDao.delete(40L);
    }
Copy the code

Test results:

Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_address as cust_add2_0_0_, customer0_.cust_industry as cust_ind3_0_0_, customer0_.cust_level as cust_lev4_0_0_, customer0_.cust_name as cust_nam5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_source as cust_sou7_0_0_, linkmans1_.lkm_cust_id as lkm_cust9_1_1_, linkmans1_.lkm_id as lkm_id1_1_1_, linkmans1_.lkm_id as lkm_id1_1_2_, linkmans1_.lkm_cust_id as lkm_cust9_1_2_, linkmans1_.lkm_email as lkm_emai2_1_2_, linkmans1_.lkm_gender as lkm_gend3_1_2_, linkmans1_.lkm_memo as lkm_memo4_1_2_, linkmans1_.lkm_mobile as lkm_mobi5_1_2_, linkmans1_.lkm_name as lkm_name6_1_2_, linkmans1_.lkm_phone as lkm_phon7_1_2_, linkmans1_.lkm_position as lkm_posi8_1_2_ from cst_customer customer0_ left outer join cst_linkman linkmans1_ on customer0_.cust_id=linkmans1_.lkm_cust_id where customer0_.cust_id=?
Hibernate: delete from cst_linkman where lkm_id=?
Hibernate: delete from cst_linkman where lkm_id=?
Hibernate: delete from cst_customer where cust_id=?
Copy the code

Note: In general, cascading deletes can be dangerous in one-to-many situations. How do YOU delete data if you are not using a cascade operation?

Delete only data from tables: you can delete data at will.

Alter table table_name alter table table_name

  • There is data from the table
    1. By default, the foreign key field is set to NULL before deletion is performed. An error will be reported if the foreign key field has a non-empty constraint from the table structure.
    2. Cascading deletion is used.
    3. Data from the secondary table should be deleted based on the foreign key value before data from the primary table is deleted.
  • No data from table: delete it freely

Iv. One-to-many Deletion (non-cascading deletion)

Create method: Delete an employee based on customer. (Using custom methods in complex queries)

package ctgu.dao;

import ctgu.pojo.Customer;
import ctgu.pojo.LinkMan;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface LinkManDao extends JpaRepository<LinkMan.Long>, JpaSpecificationExecutor<LinkMan> {
    // Delete according to the foreign key value
    public void deleteByCustomer(Customer customer);
}
Copy the code

The key mapping of the main table is to set the cascading operation:

    @OneToMany(mappedBy = "customer",fetch = FetchType.EAGER)
    private Set<LinkMan> linkMans=new HashSet<>();
Copy the code

Testing:

 package ctgu.OntoMany;

import ctgu.dao.CustomerDao;
import ctgu.dao.LinkManDao;
import ctgu.pojo.Customer;
import ctgu.pojo.LinkMan;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class OntoManyTest {
    @Autowired
    private CustomerDao customerDao;
    @Autowired
    private LinkManDao linkManDao;
@Test
    @Transactional
    @Rollback(value = false)
    public void cascadeDelete(a){
        Customer customer = customerDao.findOne(47L);
        linkManDao.deleteByCustomer(customer);
        customerDao.delete(47L); }}Copy the code

Test results:

Hibernate: select linkman0_.lkm_id as lkm_id1_1_, linkman0_.lkm_cust_id as lkm_cust9_1_, linkman0_.lkm_email as lkm_emai2_1_, linkman0_.lkm_gender as lkm_gend3_1_, linkman0_.lkm_memo as lkm_memo4_1_, linkman0_.lkm_mobile as lkm_mobi5_1_, linkman0_.lkm_name as lkm_name6_1_, linkman0_.lkm_phone as lkm_phon7_1_, linkman0_.lkm_position as lkm_posi8_1_ from cst_linkman linkman0_ left outer join cst_customer customer1_ on linkman0_.lkm_cust_id=customer1_.cust_id where customer1_.cust_id=?
Hibernate: delete from cst_linkman where lkm_id=?
Hibernate: delete from cst_linkman where lkm_id=?
Hibernate: delete from cst_customer where cust_id=?
Copy the code

5. Many-to-many in JPA

Case: Users and roles.

User: refers to a person in society.

Personas: People may have multiple identities

For example, Ming is a Java engineer, a back end siege lion, and a CEO. In addition to Xiao Ming, Java engineers also have Zhang SAN, Li Si and so on.

So we say that the relationship between the user and the role is many-to-many.

1. Establish a direct relational mapping between entity classes and tables

package ctgu.pojo;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "sys_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="user_id")
    private Long userId;
    @Column(name="user_name")
    private String userName;
    @Column(name="age")
    private Integer age;
    /** * Configure the many-to-many relationship between users and roles. * Configure the many-to-many mapping relationship. * 1. Declare the configuration of a table relationship *@ManyToMany() * targetEntity = role-class declares the other party's entity class bytecode * 2. Configure the intermediate table (two foreign keys) *@JoinTable* name: the name of the middle table * joinColumns, the position of the current object in the middle table *@JoinColumnInverseJoinColumns specifies the position of the foreign key in the middle table. */
// @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
    @ManyToMany(targetEntity = Role.class)
    @JoinTable(name = "sys_user_role".//joinColumns, the position of the current object in the middle table
            joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
            //inverseJoinColumns specifies the position of the opposing object in the middle table
            inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")})private Set<Role> roles = new HashSet<>();

    public Long getUserId(a) {
        return userId;
    }
    public void setUserId(Long userId) {
        this.userId = userId;
    }
    public String getUserName(a) {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public Integer getAge(a) {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Set<Role> getRoles(a) {
        return roles;
    }
    public void setRoles(Set<Role> roles) {
        this.roles = roles; }}Copy the code
package ctgu.pojo;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "sys_role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "role_id")
    private Long roleId;
    @Column(name = "role_name")
    private String roleName;
    @ManyToMany(targetEntity = User.class)
    @JoinTable(name = "sys_user_role".//joinColumns, the position of the current object in the middle table
            joinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")},
            //inverseJoinColumns specifies the position of the opposing object in the middle table
            inverseJoinColumns ={@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")})// @manytomany (mappedBy="roles") one of the parties should give up maintenance
    private Set<User> users = new HashSet<>();
    public Long getRoleId(a) {
        return roleId;
    }
    public void setRoleId(Long roleId) {
        this.roleId = roleId;
    }
    public String getRoleName(a) {
        return roleName;
    }
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
    public Set<User> getUsers(a) {
        return users;
    }
    public void setUsers(Set<User> users) {
        this.users = users; }}Copy the code

2. Mapping annotation description

i.@ManyToMany

Purpose: To map many-to-many relationship properties:

  • Cascade: Configures the cascade operation.
  • Fetch: Configures whether lazy loading is used.
  • TargetEntity: Entity class that configures the target. You don’t have to write it when you map many-to-many.
  • MappedBy: Specifies the name of the main table object referenced from the table entity class. (common)

ii.@JoinTable

Action: Configuration properties for intermediate tables:

  • Nam: indicates the name of the 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 middle table is associated with the primary key field of the opposite table

iii.@JoinColumn

Purpose: Defines the mapping between a primary key field and a foreign key field. Properties:

  • Name: Specifies the name of the foreign key field
  • ReferencedColumnName: Specifies the name of the primary key field referencing the primary table
  • It’s unique. Default values are not unique
  • Nullable: Specifies whether to allow null. Default value Yes.
  • Insertable: Indicates whether the insertable is allowed. Default value Yes.
  • Updatable: Indicates whether updates are allowed. Default value Yes.
  • ColumnDefinition: specifies the columnDefinition.

Many-to-many testing

I. Save users and roles

Database table :(actually can be directly generated automatically by springdataJPA)

CREATE TABLE `sys_user` (
  `user_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `age` int(11) DEFAULT NULL,
  `user_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;

CREATE TABLE `sys_role` (
  `role_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;
Copy the code

The dao interface:

package ctgu.dao;

import ctgu.pojo.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface RoleDao extends JpaRepository<Role.Long>, JpaSpecificationExecutor<Role> {}Copy the code
package ctgu.dao;

import ctgu.pojo.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface UserDao extends JpaRepository<User.Long>, JpaSpecificationExecutor<User> {}Copy the code

Test cases:

package ctgu;
import ctgu.dao.RoleDao;
import ctgu.dao.UserDao;
import ctgu.pojo.Role;
import ctgu.pojo.User;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class ManyToMany {
    @Autowired
    private UserDao userDao;
    @Autowired
    private RoleDao roleDao;

    /** * Saves a user, saves a role * many-to-many relinquishes maintenance: * passive relinquishes, whoever is selected relinquishes */
    @Test
    @Transactional
    @Rollback(false)
    public void addUserAndRole(a){
        User user = new User();
        Role role1 = new Role();
        Role role2 = new Role();
        Role role3 = new Role();
        user.setUserName("Li Daming");
        role1.setRoleName("Back end siege lion.");
        role2.setRoleName("Java programmer");
        role3.setRoleName("CEO");
        // The intermediate table can be maintained by both users and roles
        // Configure the relationship between roles and users. The data in the intermediate table can be maintained
        role1.getUsers().add(user);
        role2.getUsers().add(user);
        role3.getUsers().add(user);
        // Configure the relationship between users and roles.user.getRoles().add(role1); user.getRoles().add(role2); user.getRoles().add(role3); userDao.save(user); roleDao.save(role1); roleDao.save(role2); roleDao.save(role3); }}Copy the code

Test results:

org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
Copy the code

The reason:

In many-to-many (save), if both are set, means that both sides maintain the middle table, can insert into the middle table, the table in the middle of the two fields and joint as a primary key, so an error, the primary key repeat, solve the problems of the save failed: just on either side to give up on the table in the middle of the right to maintain, recommended in the passive side to give up, the configuration is as follows:

@manytomany (mappedBy=) @manytomany (mappedBy="roles")
private Set<SysUser> users = new HashSet<SysUser>(0);
Copy the code

Correct result:

Hibernate: insert into sys_user (age, user_name) values (?, ?)
Hibernate: insert into sys_role (role_name) values (?)
Hibernate: insert into sys_role (role_name) values (?)
Hibernate: insert into sys_role (role_name) values (?)
Hibernate: insert into sys_user_role (sys_user_id, sys_role_id) values (?, ?)
Hibernate: insert into sys_user_role (sys_user_id, sys_role_id) values (?, ?)
Hibernate: insert into sys_user_role (sys_user_id, sys_role_id) values (?, ?)
Hibernate: insert into sys_user_role (sys_role_id, sys_user_id) values (?, ?)
Copy the code

The system automatically creates the table SYS_USER_ROLE and adds data.

Ii. Cascading storage

The roles associated with the users are saved.

You only need to configure Cascade on the annotation of the action object

@ManyToMany(mappedBy = "roles",cascade = CascadeType.ALL)
    private Set<User> users = new HashSet<>();
Copy the code
package ctgu;

import ctgu.dao.RoleDao;
import ctgu.dao.UserDao;
import ctgu.pojo.Role;
import ctgu.pojo.User;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class ManyToMany {
    @Autowired
    private UserDao userDao;
    @Autowired
    private RoleDao roleDao;
    /** * Cascade operation: When saving a user and saving the associated role of the user * configure cascade */ on the annotation of the operation object
    @Test
    @Transactional
    @Rollback(false)
    public void addCasecade(a) {
        User user = new User();
        Role role = new Role();
        user.setUserName("Zhang");
        role.setRoleName("Java programmer");
        // The intermediate table can be maintained by both users and roles
        // Configure the relationship between roles and users. The data in the intermediate table can be maintained
        role.getUsers().add(user);
        // Configure the relationship between users and roles.user.getRoles().add(role); roleDao.save(role); }}Copy the code

Test results:

Hibernate: insert into sys_role (role_name) values (?)
Hibernate: insert into sys_user (age, user_name) values (?, ?)
Hibernate: insert into sys_user_role (sys_user_id, sys_role_id) values (?, ?)
Copy the code

Iii. Cascading deletion

   /** * Cascade operation: Delete user 1 and its associated object */
    @Test
    @Transactional
    @Rollback(false)
    public void deleteCasecade(a) {
        roleDao.delete(23L);
    }
Copy the code

Test results:

Hibernate: select role0_.role_id as role_id1_0_0_, role0_.role_name as role_nam2_0_0_ from sys_role role0_ where role0_.role_id=?
Hibernate: select users0_.sys_role_id as sys_role2_2_0_, users0_.sys_user_id as sys_user1_2_0_, user1_.user_id as user_id1_1_1_, user1_.age as age2_1_1_, user1_.user_name as user_nam3_1_1_ from sys_user_role users0_ inner join sys_user user1_ on users0_.sys_user_id=user1_.user_id where users0_.sys_role_id=?
Hibernate: delete from sys_user_role where sys_user_id=?
Hibernate: delete from sys_user where user_id=?
Hibernate: delete from sys_role where role_id=?
Copy the code

Note:

  • The invoked object is Role. Cascade = cascadeType. ALL;
  • Careful! May flush associated data;

6. Multi-table queries in SpringDataJPA

The following example is implemented using a one-to-many case.

I. Object navigation query

Object navigation queries navigate to their associated objects based on the loaded objects. Use entity-to-entity relationships to retrieve objects. For example, if a Customer is queried by ID, you can call the getLinkMans() method on the Customer object to get all of the Customer’s contacts.

Object navigation queries use requirements that there must be an association between two objects.

Case: Query the company and obtain all employees of the company

package ctgu.QueryTest;

import ctgu.dao.CustomerDao;
import ctgu.dao.LinkManDao;
import ctgu.pojo.Customer;
import ctgu.pojo.LinkMan;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import java.util.Set;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class ObjectQuery {
    @Autowired
    private CustomerDao customerDao;
    @Autowired
    private LinkManDao linkManDao;
    / * * * test navigation query (query an object, through the query his associations) * navigation query for object, the default use lazy loading in the form of a query, (need to query) * to invoke the get method does not immediately send the query, and when it is really associated objects using query * modify configuration, Changing lazy loading to immediate loading * FETCH requires configuring multiple table mappings to send annotations on * */
    @Test
    @Transactional// Resolve the no Session problem in Java code
    public void QueryTest01(a){
        Customer customer = customerDao.findOne(26L);
        Set<LinkMan> linkMans = customer.getLinkMans();
        for(LinkMan man:linkMans){ System.out.println(man); }}}Copy the code

Question: Do we have to find LinkMan when we query Customer?

Analysis: if we do not check, when necessary need to rewrite the code, call method query; But finding it every time wastes memory on the server.

Solution: query the main table object, using the idea of delay loading, through the configuration of the way, when we need to use the time to query.

Lazy loading

Since the object of the above call is Customer, lazy loading needs to be configured in the Customer object. The Customer object

@OneToMany(mappedBy = "customer",fetch = FetchType.LAZY)
    private Set<LinkMan> linkMans=new HashSet<>();
Copy the code

Test results:

Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_address as cust_add2_0_0_, customer0_.cust_industry as cust_ind3_0_0_, customer0_.cust_level as cust_lev4_0_0_, customer0_.cust_name as cust_nam5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_source as cust_sou7_0_0_ from cst_customer customer0_ where customer0_.cust_id=? Hibernate: select linkmans0_.lkm_cust_id as lkm_cust9_1_0_, linkmans0_.lkm_id as lkm_id1_1_0_, linkmans0_.lkm_id as lkm_id1_1_1_, linkmans0_.lkm_cust_id as lkm_cust9_1_1_, linkmans0_.lkm_email as lkm_emai2_1_1_, linkmans0_.lkm_gender as lkm_gend3_1_1_, linkmans0_.lkm_memo as lkm_memo4_1_1_, linkmans0_.lkm_mobile as lkm_mobi5_1_1_, linkmans0_.lkm_name as lkm_name6_1_1_, linkmans0_.lkm_phone as lkm_phon7_1_1_, linkmans0_.lkm_position as lkm_posi8_1_1_ from cst_linkman linkmans0_ where linkmans0_.lkm_cust_id=? LinkMan{lkmId=31, lkmName=' li si ', lkmGenger='null', lkmPhone='null', lkmMobile='null', lkmEmail='null', LkmPosition ='null', lkmMemo='null'} LinkMan{lkmId=30, lkmName=' three ', lkmGenger='null', lkmPhone='null', lkmMobile='null', lkmEmail='null', lkmPosition='null', lkmMemo='null'}Copy the code

Analysis: We found that it executed two SELECT statements.

Question: When we look up LinkMan, do we need to find out Customer?

Analysis: Because a user only belongs to one company, and each LinkMan has a unique Customer corresponding to it. If we don’t, we need extra code to query when we use it. And the query is a single object, the memory consumption is small.

Solution: from the table to use the idea of immediate loading, as long as the query from the table entity, the main table object at the same time.

Immediately load

    @OneToMany(mappedBy = "customer",fetch = FetchType.EAGER)
    private Set<LinkMan> linkMans=new HashSet<>();
Copy the code

Test results:

Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_address as cust_add2_0_0_, customer0_.cust_industry as cust_ind3_0_0_, customer0_.cust_level as cust_lev4_0_0_, customer0_.cust_name as cust_nam5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_source as cust_sou7_0_0_, linkmans1_.lkm_cust_id as lkm_cust9_1_1_, linkmans1_.lkm_id as lkm_id1_1_1_, linkmans1_.lkm_id as lkm_id1_1_2_, linkmans1_.lkm_cust_id as lkm_cust9_1_2_, linkmans1_.lkm_email as lkm_emai2_1_2_, linkmans1_.lkm_gender as lkm_gend3_1_2_, linkmans1_.lkm_memo as lkm_memo4_1_2_, linkmans1_.lkm_mobile as lkm_mobi5_1_2_, linkmans1_.lkm_name as lkm_name6_1_2_, linkmans1_.lkm_phone as lkm_phon7_1_2_, linkmans1_.lkm_position as lkm_posi8_1_2_ from cst_customer customer0_ left outer join cst_linkman linkmans1_ on customer0_.cust_id=linkmans1_.lkm_cust_id where customer0_.cust_id=? LinkMan{lkmId=30, lkmName=' z3 ', lkmGenger='null', lkmPhone='null', lkmMobile='null', lkmEmail='null', LkmPosition ='null', lkmMemo='null'} LinkMan{lkmId=31, lkmName=' lkmGenger ', lkmPhone='null', lkmMobile='null', lkmEmail='null', lkmPosition='null', lkmMemo='null'}Copy the code

Analysis result: We found that only one SELECT statement was executed.

If Set linkMans = customer.getLinkmans () is not called, Set linkMans = customer.getLinkmans () is not called. Method, the query of the associated object will not be performed.

Ii. Query using Specification

** * Specification */
	@Test
	public void testFind(a) {
		Specification<LinkMan> spec = new Specification<LinkMan>() {
			public Predicate toPredicate(Root
       
         root, CriteriaQuery
         query, CriteriaBuilder cb)
        {
				//Join stands for link query, obtained from root object
				// The first parameter is the attribute name of the associated object, and the second parameter is the connection query method (left, inner, right).
				// joinType. LEFT: LEFT JoinType.INNER: INNER JoinType.RIGHT: RIGHT JoinType
				Join<LinkMan, Customer> join = root.join("customer",JoinType.INNER);
				return cb.like(join.get("custName").as(String.class),"Wisdom Podcast 1"); }}; List<LinkMan> list = linkManDao.findAll(spec);for(LinkMan linkMan : list) { System.out.println(linkMan); }}Copy the code