1. 程式人生 > 其它 >Spring Data JPA 實現多表操作,Java常見排序演算法面試題

Spring Data JPA 實現多表操作,Java常見排序演算法面試題

linkMan.setLkmName("聯絡人1");

linkMan.setLkmGender("male");

linkMan.setLkmMobile("11111111111");

linkMan.setLkmPhone("111-11111111");

linkMan.setLkmEmail("[email protected]");

linkMan.setLkmPosition("IT講師");

linkMan.setLkmMemo("很厲害老師");



/**

 * 配置了聯絡人到客戶的關係(多對一關係)

 * 只發送了兩條儲存語句insert

 * 因為我們配置了聯絡人對客戶的對映關係(多對一)

 */

linkMan.setCustomer(customer);



/**

 * 配置了客戶到聯絡人的關係(一對多關係)

 * 從客戶角度:傳送兩條insert語句,傳送一條更新語句更新資料庫(更新外來鍵)

 * 由於我們配置了客戶到聯絡人的關係,客戶可以對外來鍵進行維護

 */

customer.getLinkMans().add(linkMan);



/**

 * 會有一條多餘的update語句

 * 由於一的一方可以維護外來鍵,會發送update語句

 * 解決此問題:只需要在一的一方放棄維護權即可

 */

//由於配置了多的一方到一的一方的關聯關係(當儲存時就已經對外來鍵賦值)

linkMan.setCustomer(customer);

//由於配置了一的一方到多的一方的關聯關係(傳送一條update語句)

customer.getLinkMans().add(linkMan);



customerDao.save(customer);

linkManDao.save(linkMan);

}




執行結果分析:



1.當我們只配置了多的一方到一的一方的關聯關係時 linkMan.setCustomer(customer);執行了兩條insert語句



2.當我們只配置了配置了一的一方到多的一方的關聯關係時customer.getLinkMans().add(linkMan);執行了兩條insert語句之後,還執行了update語句



3.當我們建立了雙向的關聯關係之後,先儲存主表,再儲存從表時:執行了兩條insert語句之後,還執行了update語句



可以看出,在設定了雙向關係之後,update語句語句是多餘的,想要解決這個問題,我們可以讓主表的一方放棄維護權,即修改Customer中的如下欄位:



//@OneToMany(targetEntity=LinkMan.class)

//@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")

//設定為

@OneToMany(mappedBy="customer")




**2\. 測試刪除資料**



@Test

@Transactional

@Rollback(false)//設定為不回滾

public void testDelete() {

customerDao.delete(1l);

}




在執行刪除操作時,對於從表資料可以隨時任意刪除,當我們要刪除主表資料資料時:



*   **1\. 有從表資料**  

    在預設情況下,它會把外來鍵欄位置為null,然後刪除主表資料。如果在資料庫的表結構上,外來鍵欄位有非空約束,預設情況就會報錯了。

    

    如果配置了放棄維護關聯關係的權利,則不能刪除(與外來鍵欄位是否允許為null, 沒有關係)因為在刪除時,它根本不會去更新從表的外來鍵欄位,這時如果還想刪除,可以使用級聯刪除引用,在實際開發中,級聯刪除請慎用!(在一對多的情況下)

    

*   **2\. 沒有從表資料引用:隨便刪**

    



**級聯操作:** 指操作一個物件同時操作它的關聯物件,只需要在操作主體的註解上配置cascade,就可以設定級聯操作



/**

  • 放棄了外來鍵的維護權

  • mappedBy:對方配置的屬性名稱(可以配置到設定多表的對映關係的註解上)

  • cascade :配置級聯

  • CascadeType.ALL 所有

  • CascadeType.MERGE 更新

  • CascadeType.PERSIST 儲存

  • CascadeType.REMOVE 刪除

*/

@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)




[](

)JPA中的多對多

---------------------------------------------------------------------



確定兩張表之間的關係,對於使用者和角色之間的關係,使用者可以表示某一個人,它可以有很多中身份,在學校可以是學生,做兼職時可以被稱作是臨時工,在家裡又有了子女的身份。同時對於某一個角色,也可以被多個使用者擁有,每個人都可以是子女,都可以是學生,都可以是臨時工。



**表關係建立:**



多對多的表關係建立靠的是中間表,其中使用者表和中間表的關係是一對多,角色表和中間表的關係也是一對多,如下圖所示:  

![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201202092752646.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0x6eTQxMDk5Mg==,size_16,color_FFFFFF,t_70#pic_left)



**實體類關係建立以及對映配置:**



在實體類中描述出兩個實體的關係,一個使用者可以具有多個角色,所以在使用者實體類中應該包含多個角色的資訊,程式碼如下:



/**

  • @Author: Ly

  • @Date: 2020-12-01 16:57

*/

@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 = "user_age")

private Integer age;



/**

 * 配置使用者到角色的多對多關係

 * 配置多對多關係

 * 1.宣告表關係的配置

 *     @ManyToMany(targetEntity = Role.class)//多對多

 *     targetEntity:代表對方的實體類位元組碼

 * 2.配置中間表(包含兩個外來鍵)

 * @JoinTable

 * name:中間表的名稱

 * joinColumns: 配置當前物件在中間表的外來鍵

 * {@JoinColumn的陣列 name:外來鍵名,referencedColumnName 參照主表的主鍵名}

 * inverseJoinColumns:配置對方物件在中間表的外來鍵

 */

@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)

@JoinTable(name = "sys_user_role",

        //joinColumns,當前物件在中間表中的外來鍵

        joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},

        //inverseJoinColumns ,對方物件在中間表中的外來鍵

        inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}

)

private Set<Role> roles=new HashSet<Role>();



/**

 * Get、Set、toString

 */

}




一個角色可以賦予多個使用者,所以在角色實體類中應該包含多個使用者的資訊,程式碼如下:



/**

  • @Author: Ly

  • @Date: 2020-12-01 16:58

*/

@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;



/**

 * 配置角色到使用者的多對多關係

 * 配置多對多關係

 * 1.宣告表關係的配置

 * 2.配置中間表

 */

// @ManyToMany(targetEntity = User.class)

// @JoinTable(name = "sys_user_role",

// //joinColumns,當前物件在中間表中的外來鍵

// joinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")},

// //inverseJoinColumns ,對方物件在中間表中的外來鍵

// inverseJoinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")}

// )

@ManyToMany(mappedBy = "roles")

private Set<User> users =new HashSet<User>();



/**

 * Get、Set、toString

 */

}




**多對多的操作:**



**新建測試類:**



/**

  • @Author: Ly

  • @Date: 2020-12-01 09:06

*/

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = "classpath:applicationContext.xml")

public class ManyToManyTest {

@Autowired

private UserDao userDao;



@Autowired

private RoleDao roleDao;

}




**1\. 測試儲存操作**



儲存使用者和角色,使用者與角色之間滿足多對多關係



/**

  • 儲存一個使用者,儲存一個角色

*/

@Test

@Transactional

@Rollback(false)

public void testAdd(){

User user=new User();

user.setUsername("張三");

user.setAge(20);



Role role=new Role();

role.setRoleName("程式設計師");



//配置使用者到角色的關係,可以對中間表中的資料進行維護

user.getRoles().add(role);

//配置角色到使用者的關係,可以對中間表中的資料進行維護

role.getUsers().add(user);



userDao.save(user);

roleDao.save(role);

}




**出現的問題:** 在多對多(儲存)中,如果雙向都設定關係,意味著雙方都維護中間表,都會往中間表插入資料,中間表的2個欄位又作為聯合主鍵,所以報錯,主鍵重複,



**解決儲存失敗的問題:** 只需要在任意一方放棄對中間表的維護權即可,推薦在被動的一方放棄,配置如下:



//放棄對中間表的維護權,解決儲存中主鍵衝突的問題

@ManyToMany(mappedBy="roles")

private Set users = new HashSet();




**級聯操作:**



/**

  • 放棄了外來鍵的維護權

  • mappedBy:對方配置的屬性名稱(可以配置到設定多表的對映關係的註解上)

  • cascade :配置級聯

  • CascadeType.ALL 所有

  • CascadeType.MERGE 更新

  • CascadeType.PERSIST 儲存

  • CascadeType.REMOVE 刪除

*/

@ManyToMany(mappedBy = "customer",cascade = CascadeType.ALL)




**測試級聯操作:**



/**

  • 測試級聯新增(儲存一個使用者的同時儲存關聯的角色)

*/

@Test

@Transactional

@Rollback(false)

public void testCascadeAdd(){

User user=new User();

user.setUsername("張三");

user.setAge(20);



Role role=new Role();

role.setRoleName("程式設計師");



//配置使用者到角色的關係,可以對中間表中的資料進行維護

user.getRoles().add(role);

//配置角色到使用者的關係,可以對中間表中的資料進行維護

role.getUsers().add(user);



userDao.save(user);

}

/**

  • 測試級聯刪除(禁用)

  • 在多對多的刪除時,雙向級聯刪除根本不能配置

  • 如果配了的話,如果資料之間有相互引用關係,可能會清空所有資料

*/

@Test

@Transactional

@Rollback(false)

public void testCascadeRemove(){

testCascadeAdd();

//查詢1號使用者

User user=userDao.findOne(1l);

//刪除1號使用者

userDao.delete(user);

}




[](

)Spring Data JPA中的多表查詢

----------------------------------------------------------------------------------



**1\. 物件導航查詢:**



物件圖導航檢索方式是根據已經載入的物件,導航到他的關聯物件。它利用類與類之間的關係來檢索物件。例如:我們通過ID查詢方式查出一個客戶,可以呼叫Customer類中的getLinkMans()方法來獲取該客戶的所有聯絡人。物件導航查詢的使用要求是:兩個物件之間必須存在關聯關係。



**新建測試類:**



/**

  • @Author: Ly

  • @Date: 2020-12-01 22:05

*/

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = "classpath:applicationContext.xml")

public class ObjectQueryTest {

@Autowired

private CustomerDao customerDao;



@Autowired

private LinkManDao linkManDao;

}




查詢一個客戶,獲取該客戶下的所有聯絡人



//由於是在java程式碼中測試,為了解決no session問題,將操作配置到同一個事務中

//測試物件導航查詢

@Test

@Transactional //解決在Java程式碼中的no session問題

public void testQuery1(){

//查詢id為1的客戶

Customer customer=customerDao.getOne(1l);

//物件導航查詢,此客戶下的所有聯絡人

Set<LinkMan> linkMans = customer.getLinkMans();



for (LinkMan linkMan:linkMans){

    System.out.println(linkMan);

}

}

//測試物件導航查詢

@Test

@Transactional //解決在Java程式碼中的no session問題

public void testQuery2(){

//查詢id為1的客戶

Customer customer=customerDao.findOne(1l);

//物件導航查詢,此客戶下的所有聯絡人

Set<LinkMan> linkMans = customer.getLinkMans();



for (LinkMan linkMan:linkMans){

    System.out.println(linkMan);

}

}




**物件導航查詢的問題分析:**



問題1:我們查詢客戶時,要不要把聯絡人查詢出來?



如果我們不查的話,在用的時候還要自己寫程式碼,呼叫方法去查詢。如果我們查出來的,不使用時又會白白的浪費了伺服器記憶體。



解決:採用延遲載入的思想。通過配置的方式來設定當我們在需要使用時,發起真正的查詢。



/**

  • 在客戶物件的@OneToMany註解中新增fetch屬性

  •  FetchType.EAGER	:立即載入
    
  •  FetchType.LAZY	:延遲載入
    

*/

@OneToMany(mappedBy="customer",fetch=FetchType.EAGER)

private Set linkMans = new HashSet<>();




問題2:我們查詢聯絡人時,要不要把客戶查詢出來?



例如:查詢聯絡人詳情時,肯定會看看該聯絡人的所屬客戶。如果我們不查的話,在用的時候還要自己寫程式碼,呼叫方法去查詢。如果我們查出來的話,一個物件不會消耗太多的記憶體。而且多數情況下我們都是要使用的。



解決: 採用立即載入的思想。通過配置的方式來設定,只要查詢從表實體,就把主表實體物件同時查出來



/**

  • 在聯絡人物件的@ManyToOne註解中新增fetch屬性

  •  FetchType.EAGER	:立即載入
    
  •  FetchType.LAZY	:延遲載入
    

*/

@ManyToOne(targetEntity=Customer.class,fetch=FetchType.EAGER)

@JoinColumn(name="cst_lkm_id",referencedColumnName="cust_id")

private Customer customer;




**2\. 使用Specification查詢**



/**

  • Specification的多表查詢

*/

@Test

public void testFind() {

Specification<LinkMan> spec = new Specification<LinkMan>() {

總結

在這裡,由於面試中MySQL問的比較多,因此也就在此以MySQL為例為大家總結分享。但是你要學習的往往不止這一點,還有一些主流框架的使用,Spring原始碼的學習,Mybatis原始碼的學習等等都是需要掌握的,我也把這些知識點都整理起來了

CodeChina開源專案:【一線大廠Java面試題解析+核心總結學習筆記+最新講解視訊】