使用Hibernate、JPA、Lombok遇到的有趣問題
前言
先用我不是藥神電影海報鎮樓,這個電影真心不錯,推薦大家。
image.png
準備
講解Hibernate之前,首先建立兩個實體類,一個是Student類,一個School類。School和Student的關係是一對多的關係
@Entity@Table(name = "tbl_school")@Datapublic class School { @Id @GenericGenerator(name = "idGenerator", strategy = "uuid") @GeneratedValue(generator = "idGenerator") @Column(name = "id") private String id; @Column(name = "school_name") private String schoolName; @OneToMany(mappedBy = "school", fetch = FetchType.LAZY, cascade = CascadeType.ALL) private SetstudentList = new HashSet(); @Column(name = "created_dt") private Date createdDt; @Column(name = "updated_dt") private Date updatedDt; @Column(name = "is_del") private String isDel; }
@Entity@Table(name = "tbl_student")@Datapublic class Student { @Id @GenericGenerator(name = "idGenerator", strategy = "uuid") @GeneratedValue(generator = "idGenerator") @Column(name = "id") private String id; @Column(name = "student_name") private String studentName; @Column(name = "school_id", insertable = false, updatable = false) private String schoolId; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "school_id") private School school; @Column(name = "created_dt") private Date createdDt; @Column(name = "updated_dt") private Date updatedDt; @Column(name = "is_del") private String isDel; }
基礎概念
主鍵採用UUID策略
@Id @GenericGenerator(name = "idGenerator", strategy = "uuid") @GeneratedValue(generator = "idGenerator") @Column(name = "id")
Fetch用於關聯關係,作用域為讀取操作
@OneToMany預設的是FetchType.LAZY(懶載入)
@ManyToOne預設的是FetchType.EAGER(急載入)
由於一個School有多個Student,我們可以用@OneToMany去維護這種關係。類似的還有@OneToOne、@ManyToOne,@ManyToMany這些註解。值得注意的話,mappedBy只能適用於@OneToOne,@OneToMany,@ManyToMany這些註解。mappedBy用於主表的一方。對於我們來說School就是主表,Student就是從表。一對多的關係由從表去負責維護。
@OneToMany(mappedBy = "school", fetch = FetchType.LAZY, cascade = CascadeType.ALL) private SetstudentList = new HashSet();
再說說與mappedBy互斥的@JoinColumn註解,@JoinColumn用於擁有主表外來鍵的一方,也就是從表。
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "school_id") private School school;
mappedBy屬性應該指向從表中維護與主表關係的欄位。對於School類來說,mappedBy就應該指向Student類中的school屬性。
為了讓主表知道從表中的那些欄位關聯自己,在主表一方可以用mappedBy指向從表中的一個關聯到自己的物件。在從表一方可以用@JoinColumn註解以外來鍵欄位的形式關聯到主表。
Cascade用於級聯,作用域為增刪改操作。CascadeType.ALL包含所有級聯策略。(後面會具體演示不同級聯策略的效果,加深理解)
public enum CascadeType { /** Cascade all operations */ ALL, /** Cascade persist operation */ PERSIST, /** Cascade merge operation */ MERGE, /** Cascade remove operation */ REMOVE, /** Cascade refresh operation */ REFRESH, /** * Cascade detach operation * * @since Java Persistence 2.0 * */ DETACH }
toString()方法造成的死迴圈
我們去查詢一個學生,看其否則用了懶載入策略
@Test public void query() { Student student = studentDao.findOne("1"); System.out.println("student=" + student); }
結果丟擲了這樣的異常...
org.hibernate.LazyInitializationException: could not initialize proxy - no Session at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:148) at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:266) at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73) at cmazxiaoma.model.School_$$_jvstaa_0.toString(School_$$_jvstaa_0.java)
Hibernate跟Spring整合了,Hibernate的Session就交付給Spring去管理。每次資料庫操作後,會關閉Session,當我們想要用懶載入方式去獲得資料的時候,原來的Session已經關閉,不能獲取資料,所以會丟擲這樣的異常。
我們可以透過Spring提供的OpenSessionInViewFilter去解決這種問題,將Hibernate的Session繫結到整個執行緒的Servlet過濾器去處理請求,而它必須依賴於Servlet容器,不適用於我們的單元測試。
@Configurationpublic class FilterConfig { /** * 解決hibernate懶載入出現的no session問題 * @return */// @Bean// public FilterRegistrationBean filterRegistrationBean() {// FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();// filterRegistrationBean.setFilter(new OpenSessionInViewFilter());// filterRegistrationBean.addInitParameter("urlPatterns", "/*");// return filterRegistrationBean;// } /** * 解決jpa 懶載入出現的no session問題 * @return */ @Bean public FilterRegistrationBean filterRegistrationBean() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new OpenEntityManagerInViewFilter()); filterRegistrationBean.addInitParameter("urlPatterns", "/*"); return filterRegistrationBean; } }
我們可以在application-dev.properties配置如下程式碼,就可以在Servlet容器和單元測試中使用懶載入策略了。
#將jpa的session繫結到整個執行緒的Servlet過濾器,處理請求spring.jpa.open-in-view=truespring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
注意喲,Hibernate依賴SessionFactory去建立Session例項,而JPA依賴於EntityManagerFactory去建立EntityManager例項。
解決了Could not initialize proxy - no session
的異常,我們再去跑一下單元測試,出現了更大的錯誤"StackOverflowError"
java.lang.StackOverflowError at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:131) at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:108) at org.apache.tomcat.jdbc.pool.interceptor.AbstractCreateStatementInterceptor.invoke(AbstractCreateStatementInterceptor.java:75) at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:108)
我們可以透過日誌看到sql的輸出,發現了sql重複執行了好多次。以下我擷取了前10條sql記錄。
Hibernate: select student0_.id as id1_1_0_, student0_.created_dt as created_2_1_0_, student0_.is_del as is_del3_1_0_, student0_.school_id as school_i4_1_0_, student0_.student_name as student_5_1_0_, student0_.updated_dt as updated_6_1_0_ from tbl_student student0_ where student0_.id=? Hibernate: select school0_.id as id1_0_0_, school0_.created_dt as created_2_0_0_, school0_.is_del as is_del3_0_0_, school0_.school_name as school_n4_0_0_, school0_.updated_dt as updated_5_0_0_ from tbl_school school0_ where school0_.id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=? Hibernate: select school0_.id as id1_0_0_, school0_.created_dt as created_2_0_0_, school0_.is_del as is_del3_0_0_, school0_.school_name as school_n4_0_0_, school0_.updated_dt as updated_5_0_0_ from tbl_school school0_ where school0_.id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=?
透過觀察發現,第一條sql是執行查詢Student的sql,第二條sql是執行查詢School的sql,第三條sql是執行School裡面所有學生的sql,第四條sql是執行查詢School的sql,後面所有的sql都是執行查詢School裡面所有學生的sql。
很明顯發生了迴圈依賴的情況。Lombok的@Data相當於@Getter、@Setter、@ToString、@EqualsAndHashCode、@RequiredArgsConstructor註解。
如果我們去掉System.out.println("student=" + student);
這行程式碼,再去跑單元測試,會發現沒有報錯。
@Test public void query() { Student student = studentDao.findOne("1"); System.out.println("student=" + student); }
image.png
我們可以將迴圈引用的問題定位到Student和School類的toString()方法。Lombok的@Data註解為我們生成的toString()覆蓋了整個類的屬性。
// School類 @Override public String toString() { return "School{" + "id='" + id + ''' + ", schoolName='" + schoolName + ''' + ", studentList=" + studentList + ", createdDt=" + createdDt + ", updatedDt=" + updatedDt + ", isDel='" + isDel + ''' + '}'; } // Student類 @Override public String toString() { return "Student{" + "id='" + id + ''' + ", studentName='" + studentName + ''' + ", schoolId='" + schoolId + ''' + ", school=" + school + ", createdDt=" + createdDt + ", updatedDt=" + updatedDt + ", isDel='" + isDel + ''' + '}'; }
我們可以確認System.out.println("student=" + student);
會呼叫Student類中toString()方法,toString()方法會觸發school屬性的懶載入,便會去呼叫School類的toString()方法,School()類中的toString()方法,會觸發studentList屬性的懶載入,接著會呼叫Student類中的toString()方法。以上就是迴圈引用的過程。
image.png
我們將@Data註解去掉,換成@Setter、@Getter、@EqualsAndHashCode註解。我們自己重寫Student類和School類的toString()方法。
// School類 @Override public String toString() { return "School{" + "id='" + id + ''' + ", schoolName='" + schoolName + ''' + '}'; } // Student類 @Override public String toString() { return "Student{" + "id='" + id + ''' + ", studentName='" + studentName + ''' + '}'; }
再去跑查詢Student的測試用例。
@Test public void query() { Student student = studentDao.findOne("1"); System.out.println("student=" + student); }
我們發現輸出Student的資訊,並沒有去查詢School的資訊。證明懶載入策略起了作用。
Hibernate: select student0_.id as id1_1_0_, student0_.created_dt as created_2_1_0_, student0_.is_del as is_del3_1_0_, student0_.school_id as school_i4_1_0_, student0_.student_name as student_5_1_0_, student0_.updated_dt as updated_6_1_0_ from tbl_student student0_ where student0_.id=? student=Student{id='1', studentName='捲毛'}
當我們去訪問Student的School詳情資訊時,才會去查詢School資訊。
@Test public void query() { Student student = studentDao.findOne("1"); System.out.println("student=" + student); School school = student.getSchool(); System.out.println("school=" + school); }
Hibernate: select student0_.id as id1_1_0_, student0_.created_dt as created_2_1_0_, student0_.is_del as is_del3_1_0_, student0_.school_id as school_i4_1_0_, student0_.student_name as student_5_1_0_, student0_.updated_dt as updated_6_1_0_ from tbl_student student0_ where student0_.id=? student=Student{id='1', studentName='捲毛'} Hibernate: select school0_.id as id1_0_0_, school0_.created_dt as created_2_0_0_, school0_.is_del as is_del3_0_0_, school0_.school_name as school_n4_0_0_, school0_.updated_dt as updated_5_0_0_ from tbl_school school0_ where school0_.id=? school=School{id='1', schoolName='WE學校'}
hashCode()方法造成的死迴圈
我們去查詢School的資訊
@Test public void query() throws Exception { School school = schoolDao.findOne("1"); System.out.println(school); SetstudentList = school.getStudentList(); System.out.println("studentList=" + studentList); }
特麼,又發現了死迴圈。我們可以發現執行了查詢學校資訊的sql,成功輸出了學習資訊後,才發生死迴圈。
Hibernate: select school0_.id as id1_0_0_, school0_.created_dt as created_2_0_0_, school0_.is_del as is_del3_0_0_, school0_.school_name as school_n4_0_0_, school0_.updated_dt as updated_5_0_0_ from tbl_school school0_ where school0_.id=? School{id='1', schoolName='WE學校'} Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=? Hibernate: select school0_.id as id1_0_0_, school0_.created_dt as created_2_0_0_, school0_.is_del as is_del3_0_0_, school0_.school_name as school_n4_0_0_, school0_.updated_dt as updated_5_0_0_ from tbl_school school0_ where school0_.id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=? Hibernate: select studentlis0_.school_id as school_i4_1_0_, studentlis0_.id as id1_1_0_, studentlis0_.id as id1_1_1_, studentlis0_.created_dt as created_2_1_1_, studentlis0_.is_del as is_del3_1_1_, studentlis0_.school_id as school_i4_1_1_, studentlis0_.student_name as student_5_1_1_, studentlis0_.updated_dt as updated_6_1_1_ from tbl_student studentlis0_ where studentlis0_.school_id=?
透過進一步,看到棧異常的錯誤定位在School類和Student類中的hashCode()。
java.lang.StackOverflowError at cmazxiaoma.model.School.hashCode(School.java:22) at sun.reflect.GeneratedMethodAccessor38.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:84) at cmazxiaoma.model.School_$$_jvstc33_0.hashCode(School_$$_jvstc33_0.java) at cmazxiaoma.model.Student.hashCode(Student.java:20)
那Student和School類中的hashCode()還在什麼情況下呼叫呢? studentList是Set集合,HashSet內部實現其實是透過HashMap,HashSet的元素其實就是內部HashMap的key,HashMap的key不能重複決定了HashSet的元素不能重複。我們往HashSet裡面新增元素時,其實會呼叫hashCode()和equals()確定元素在HashMap儲存的具體位置。
@OneToMany(mappedBy = "school", fetch = FetchType.LAZY, cascade = CascadeType.ALL) private SetstudentList = new HashSet();
透過反編譯School類和Student類,我們發現它們的hashCode()方法存在迴圈引用。
看School類中的hashCode()方法,studentList是一個HashSet集合,HashSet集合的hashCode()計算方式會遍歷所有元素,累加求和每個元素的hashCode值。但是studentList裡面元素的型別是Student,Student類中的hashCode()又會依賴於School類的hashCode()方法,這樣就形成了迴圈依賴。
// School類的hashCode()方法 public int hashCode() { int PRIME = true; int result = 1; Object $id = this.getId(); int result = result * 59 + ($id == null ? 43 : $id.hashCode()); Object $schoolName = this.getSchoolName(); result = result * 59 + ($schoolName == null ? 43 : $schoolName.hashCode()); Object $studentList = this.getStudentList(); result = result * 59 + ($studentList == null ? 43 : $studentList.hashCode()); Object $createdDt = this.getCreatedDt(); result = result * 59 + ($createdDt == null ? 43 : $createdDt.hashCode()); Object $updatedDt = this.getUpdatedDt(); result = result * 59 + ($updatedDt == null ? 43 : $updatedDt.hashCode()); Object $isDel = this.getIsDel(); result = result * 59 + ($isDel == null ? 43 : $isDel.hashCode()); return result; } // Student類中的hashCode()方法 public int hashCode() { int PRIME = true; int result = 1; Object $id = this.getId(); int result = result * 59 + ($id == null ? 43 : $id.hashCode()); Object $studentName = this.getStudentName(); result = result * 59 + ($studentName == null ? 43 : $studentName.hashCode()); Object $schoolId = this.getSchoolId(); result = result * 59 + ($schoolId == null ? 43 : $schoolId.hashCode()); Object $school = this.getSchool(); result = result * 59 + ($school == null ? 43 : $school.hashCode()); Object $createdDt = this.getCreatedDt(); result = result * 59 + ($createdDt == null ? 43 : $createdDt.hashCode()); Object $updatedDt = this.getUpdatedDt(); result = result * 59 + ($updatedDt == null ? 43 : $updatedDt.hashCode()); Object $isDel = this.getIsDel(); result = result * 59 + ($isDel == null ? 43 : $isDel.hashCode()); return result; }
HashSet的hashCode()方法來自與父類AbstractSet。
public int hashCode() { int h = 0; Iteratori = iterator(); while (i.hasNext()) { E obj = i.next(); if (obj != null) h += obj.hashCode(); } return h; }
既然發現了是@Data註解生成的hashCode()方法坑了我們,那我們自己重寫Student和Teacher類中的hashCode()和equals()方法
@Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof School)) return false; if (!super.equals(o)) return false; School school = (School) o; if (!getId().equals(school.getId())) return false; if (!getSchoolName().equals(school.getSchoolName())) return false; if (!getCreatedDt().equals(school.getCreatedDt())) return false; if (!getUpdatedDt().equals(school.getUpdatedDt())) return false; return getIsDel().equals(school.getIsDel()); } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + getId().hashCode(); result = 31 * result + getSchoolName().hashCode(); result = 31 * result + getCreatedDt().hashCode(); result = 31 * result + getUpdatedDt().hashCode(); result = 31 * result + getIsDel().hashCode(); return result; }
@Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Student)) return false; Student student = (Student) o; if (!getId().equals(student.getId())) return false; if (!getStudentName().equals(student.getStudentName())) return false; if (!getSchoolId().equals(student.getSchoolId())) return false; if (!getCreatedDt().equals(student.getCreatedDt())) return false; if (!getUpdatedDt().equals(student.getUpdatedDt())) return false; return getIsDel().equals(student.getIsDel()); } @Override public int hashCode() { int result = getId().hashCode(); result = 31 * result + getStudentName().hashCode(); result = 31 * result + getSchoolId().hashCode(); result = 31 * result + getCreatedDt().hashCode(); result = 31 * result + getUpdatedDt().hashCode(); result = 31 * result + getIsDel().hashCode(); return result; }
記住我們重寫equals()方法,就必須要重寫hashCode()方法。可以看到Student類和School類都有id、createdDt、updatedDt、isDel的屬性,我們如果把這些相同屬性都提到父類中,讓Student類和School類繼承這個父類,同時使用@EqualsAndHashCode註解為其生成equals()和hashCode()方法。那麼會出現一個問題,在比較物件是否相等時會得出錯誤的結果。因為@EqualsAndHashCode生成的equals()和hashCode()沒有使用父類的屬性。接下來,我們就測試一下吧。
@EqualsAndHashCode的坑
定義一個Father類。
@Getter@Setter@EqualsAndHashCodepublic class Son extends Father { private String sonName; }
定義一個Son類。
@Getter@Setter@EqualsAndHashCodepublic class Son extends Father { private String sonName; }
我們執行下面的程式碼,比較son1和son2物件是否相等。結果返回true,很顯然只比較Son物件的屬性,沒有比較Son的父類Father裡面的屬性。
public class SonTest { @Test public void test() { Son son1 = new Son(); son1.setSonName("son1"); son1.setFatherName("baseFather"); Son son2 = new Son(); son2.setSonName("son1"); son2.setFatherName("baseFather2"); System.out.println(son1.equals(son2)); } }
image.png
檢視反編譯後的Son類程式碼,恍然大悟。
public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof Son)) { return false; } else { Son other = (Son)o; if (!other.canEqual(this)) { return false; } else { Object this$sonName = this.getSonName(); Object other$sonName = other.getSonName(); if (this$sonName == null) { if (other$sonName != null) { return false; } } else if (!this$sonName.equals(other$sonName)) { return false; } return true; } } } public int hashCode() { int PRIME = true; int result = 1; Object $sonName = this.getSonName(); int result = result * 59 + ($sonName == null ? 43 : $sonName.hashCode()); return result; }
專案地址
尾言
在沒有真正理解框架幹了什麼之前,不要對框架充分信任。我們要明白Lombok框架幹了什麼,不然出現一堆問題就懵逼了。
作者:cmazxiaoma
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/818/viewspace-2810372/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 使用Spring Data Jpa遇到問題彙總Spring
- lombok編譯遇到“找不到符號的問題”Lombok編譯符號
- lombok+JPA可能會出什麼麼問題? - jpabuddyLombok
- 我遇到的有趣面試題:破解程式面試題
- 使用git遇到的問題Git
- ueditor使用遇到的問題
- lombok版本 與 lombok plugins版本問題LombokPlugin
- mapstruct結合lombok使用的衝突問題StructLombok
- 解決Spring Data JPA Hibernate的N+1問題的最佳方法Spring
- Go mod 使用遇到的問題Go
- laravel使用中遇到的問題Laravel
- c++使用遇到的問題C++
- 使用 redisson 時遇到的問題Redis
- Lombok生成get/set異常問題(Lombok缺陷)Lombok
- 如何使用Hibernate/JPA的JPQL/HQL查詢提取?
- 【MySQL】使用innobackup 2.4遇到的問題MySql
- 使用javap -v 命令遇到的問題Java
- MySQL 使用 JPA + Hibernate 的 9 個高效能技巧MySql
- 使用CodeMirror外掛遇到的問題
- 使用git add 遇到的小問題Git
- Vue使用中遇到的程式碼問題Vue
- Linux ~ CentOS使用中遇到的問題LinuxCentOS
- 記錄使用Performance API遇到的問題ORMAPI
- CKEditor使用中遇到的問題解決
- Fragstas軟體使用中遇到的問題
- 解決在使用Amoeba遇到的問題
- 關於MQTT 使用遇到問題MQQT
- Hibernate的session問題Session
- hibernate 中使用hbm2java 的問題Java
- lombok的使用Lombok
- 請高手看一下,遇到Hibernate3和weblogic的問題!Web
- 寫了MVC+HIBERNATE,遇到一個問題,請高手賜教MVC
- 工作遇到的問題
- mysql 遇到的問題MySql
- 使用資料泵遷移遇到的問題
- java Gson使用中遇到的Date格式問題Java
- 使用nagios所遇到的問題簡記iOS
- 使用java.lang.reflect.Method遇到的問題Java