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
**級聯操作:**
/**
-
放棄了外來鍵的維護權
-
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
問題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原始碼的學習等等都是需要掌握的,我也把這些知識點都整理起來了