toString(卡塔尔方法变成的死循环

笔者们去查询二个学子,看其不然用了懒加载战术

 @Test public void query() { Student student = studentDao.findOne; 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重复实行了好数12回。以下作者截取了前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; System.out.println("student=" + student); }

图片 1image.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(卡塔尔方法。以上即是循环援用的进度。

图片 2image.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; 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; 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学校'}

一、Lombok是什么

Lombok是后生可畏款小巧的代码生成工具。官网:[http://projectlombok.org/

LomBok首要特征有:自动生成暗中同意的getter/setter方法、自动化的财富处理(通过@Cleanup评释)及评释驱动的特别管理等。如今在国外广泛应用。

LomBok它和jquery同样,目标是让程序员写更少的代码,以至修改一些原始语法中可惜之处。Lombok能实现那或多或少。既不是用annotations
process,亦不是用反射。而是向来黑到了编译进程中。所以对运作作用未有别的影响,大家可以通过反编写翻译class文件实行表明。

简介

Lombok
是大器晚成款极其实用的Java
工具,它提供了简要的笺注的样式来援救我们简化撤废一些必须有但显得很丰腴的Java
代码,特别是本着于
POJO,创设诸如Setter/Getter、Constructor、toString(卡塔尔、hashCode(卡塔尔和equals(卡塔尔(قطر‎那样的诀窍。

利用例子

增加信任库

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>compile</scope>
</dependency>

添加 Lombok 注解

  • @Data:证明在类上,提供类属性的 getting 和 setting
    方法,还提供了 equals 、canEqual、hashCode、toString 方法
  • @Setter:申明在性质上,为属性提供 setting 方法
  • @Getter:申明在性质上,为属性提供 getting 方法
  • @Log4j:证明在类上,为类提供三个属性名称叫 log 的 log4j 日志对象
  • @NoArgsConstructor:声明在类上,为类提供二个无参的构造方法
  • @AllArgsConstructor:注脚在类上,为类提供多个全参的构造方法
  • @Builder:评释在类上,提供流式营造一个目标实例

Example.builder().foo(1).bar(“test”).build()
  • @EqualsAndHashCode:实现equals()方法和hashCode()方法
  • @ToString:实现toString()方法

@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.setFatherName("baseFather"); Son son2 = new Son(); son2.setSonName; son2.setFatherName("baseFather2"); System.out.println(son1.equals; }}

图片 3image.png

翻开反编写翻译后的Son类代码,柳暗花明。

 public boolean equals { if (o == this) { return true; } else if (!(o instanceof Son)) { return false; } else { Son other = o; if (!other.canEqual { 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; }

2. Lombok的使用

Lombok的特征是依据annotation创立一些代码,以调控食复代码的数目,它提供了以下多少个annotation:

@Getter和@Setter:为属性创设getter和setter

@EqualsAndHashCode:实现equals()方法和hashCode()方法

@ToString:实现toString()方法

@Data:上述3个annotation的和,会创建getter setter equals
hashCode和toString

@Cleanup:关闭流

@Synchronized:对象同步

@SneakyThrows:抛出十一分

@Log4j: log4j日志表明

上边,看一些实例吧:

@Getter @Setter

原本的写法:

图片 4

2.png

Lombok中的写法

图片 5

3.png

在变化 getter/setter 方法时,Lombok 据守观念的正统。全体那个方法名都是get 或 set
开班并且属性名都以大写的。当然,即使属性是三个Boolean,处境差异。在此种场地下,getter 以 is 发轫,而非 get。那是 Java
bean
的生机勃勃种标准实施;

hashcode 当JavaBean 输出数字;

toString 输出Class 和各样属性的打印列表;

修饰Getter方法的访谈权限@Getter(AccessLevel.PROTECTED卡塔尔修饰getter方法的访谈权限
@Setter(AccessLevel.PROTECTED卡塔尔 修饰setter方法的拜访权限
@Getter(AccessLevel.PROTECTED) private int size;

@ToString

图片 6

4.png

图片 7

5.png

消灭某一个一定字段重写toString方法:

@ToString(exclude=”color”)public @Data class Lure {private String
name;private int size;private String color;private String style;
}

@EqualsAndHashCode

图片 8

6.png

等等……使用起来非常方便.

Lombok features

  • val:用在一些变量前面,也即是将变量注脚为final
  • @NonNull:给艺术参数增添那一个注脚会活动在章程内对该参数举办是还是不是为空的校验,就算为空,则抛出NPE(NullPointerException);
  • @Cleanup机关管理资源,用留意气风发部分变量从前,在此时此刻变量范围内将在实践达成退出从前会自行清理能源(调用close方法);
  • @Getter /
    @Setter:用在品质上,再也不用本人手写setter和getter方法了,还足以钦命访谈范围;
  • @ToString:用在类上,能够自动覆写toString方法,当然还足以加别的参数,举例@ToString(exclude=”id”卡塔尔国消释id属性;
  • @EqualsAndHashCode:用在类上,自动生成equals方法和hashCode方法;
  • @NoArgsConstructor、@RequiredArgsConstructor

    和@AllArgsConstructor:用在类上,自动生成无参结构和应用具备参数的布局函数以至把富有@NonNull属性作为参数的构造函数,假使钦命staticName

    “of”参数,同一时间还大概会生成贰个重临类对象的静态工厂方法,比接受布局函数方便广大;

  • @Data:注解在类上,也便是同有时候使用了@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstrutor这一个表明,对于POJO类十三分有用;
  • @Value
  • @Builder用在类、结构器、方法上,为您提供复杂的builder
    APIs;
  • @Getter(lazy=true)
    懒加载,自动生成Double Check Lock样品代码;
  • @Log:依据分化的笺注生成分歧类型的log对象,不过实例名称都是log

概述

  • Lombok 提供了黄金年代部分讲授来增加援助大家简化杀绝一些亟须有但显得丰腴的 java
    代码,如 getting 和
    setting,它是因此编写翻译时字节码改进产生,品质与手写代码是相通的

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图