2017-02-16 27 views
-1

美好的一天,所有。我是Spring Data + JPA的新手。我需要你的帮助。 这是我在stackoverflow上的第一个问题,对不起,如果我形成我的问题不正确。春季数据JPA +休眠 - 无法初始化代理 - 无修正后的会话:

我开始用Spring Data + JPA + Hibernate,Spring MVC,Use MySQL来实现项目。

我有DB计划:

项目的DB

DB scheme

应用程序上下文:

<context:property-placeholder location="classpath:util.properties" /> 
<!--Activates various annotations to be detected in bean classes: Spring's @Required and @Autowired and so on--> 
<context:annotation-config/> 

<!-- Datasource. - MySQL --> 
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
    <property name="driverClassName" value="${jdbc.driverClass}"/> 
    <property name="url" value="${jdbc.url}" /> 
    <property name="username" value="${jdbc.username}"/> 
    <property name="password" value="${jdbc.password}" /> 
</bean> 

<!--Do not forget activate @Transactional JPA annotation with <annotation-driven/>--> 
<!-- JPA Persistence Context and EntityManager configuration --> 
<bean id="entityManagerFactory" 
     class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" > 
    <!--packagesToScan - search Entity and mapping them --> 
    <property name="packagesToScan" value="by.GetItFree" /> 
    <property name="dataSource" ref="dataSource" /> 
    <property name="jpaVendorAdapter"> 
     <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" > 
      <property name="generateDdl" value="true" /> 
      <property name="showSql" value="true" /> 
     </bean> 
    </property> 
    <property name="jpaProperties"> 
     <props> 
      <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> 
      <prop key="hibernate.show_sql">true</prop> 
      <prop key="hibernate.format_sql">false</prop> 
      <prop key="hibernate.hbm2ddl.auto">update</prop> 
      <prop key="hibernate.enable_lazy_load_no_trans">true</prop> 
     </props> 
    </property> 
</bean> 

<!-- Automatic Transaction Participation--> 
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
    <property name="entityManagerFactory" ref="entityManagerFactory" /> 
</bean> 

<jpa:repositories base-package="by.GetItFree.orm.repository" entity-manager-factory-ref="entityManagerFactory" 
        transaction-manager-ref="transactionManager"/> 

MVC配置:

<!-- 
    mvc:annotation-driven configures Spring MVC annotations 
    Support for validating @Controller inputs with @Valid, if a JSR-303 Provider is present on the classpath. 
    HttpMessageConverter support for @RequestBody method parameters and @ResponseBody method return values 
    from @RequestMapping or @ExceptionHandler methods. 
--> 
<mvc:annotation-driven/> 

<!-- activate @Transactional JPA annotation --> 
<tx:annotation-driven/> 

<!-- ViewResolver bean config for mapping strings to jsp views --> 
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
    <!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' --> 
    <property name="order" value="1" /> 
    <property name="prefix" value="/WEB-INF/view" /> 
    <property name="suffix" value=".jsp" /> 
</bean> 


<mvc:view-controller path="/about.html" view-name="/about/about"/> 
<mvc:view-controller path="/index.html" view-name="/index"/> 


<!-- Static Resources Configuration (get access to static sources such as CSS and JavaScript files) --> 
<mvc:resources mapping="/resources/**" location="/resources/" /> 


一些JPA持久性的entites的:

广告:

@Entity 
public class Advert { 
    private int id; 
    private String karmaReq; 
    private byte[] image; 
    private int profileId; 
    private String profileUsersUsername; 
    private String head; 
    private String content; 
    private byte ordered; 
    private Timestamp date; 
    private Profile profile; 
    private Collection<Comment> commentsById; 

    @Id 
    @Column(name = "id", nullable = false) 
    public int getId() { 
     return id; 
    } 

public void setId(int id) { 
    this.id = id; 
} 

@Basic 
@Column(name = "karmaReq", nullable = true, length = 45) 
public String getKarmaReq() { 
    return karmaReq; 
} 

public void setKarmaReq(String karmaReq) { 
    this.karmaReq = karmaReq; 
} 

@Basic 
@Column(name = "image", nullable = false) 
public byte[] getImage() { 
    return image; 
} 

public void setImage(byte[] image) { 
    this.image = image; 
} 

@Basic 
@Column(name = "profile_id", nullable = false) 
public int getProfileId() { 
    return profileId; 
} 

public void setProfileId(int profileId) { 
    this.profileId = profileId; 
} 

@Basic 
@Column(name = "profile_users_username", nullable = false, length = 45) 
public String getProfileUsersUsername() { 
    return profileUsersUsername; 
} 

public void setProfileUsersUsername(String profileUsersUsername) { 
    this.profileUsersUsername = profileUsersUsername; 
} 

@Basic 
@Column(name = "head", nullable = true, length = 45) 
public String getHead() { 
    return head; 
} 

public void setHead(String head) { 
    this.head = head; 
} 

@Basic 
@Column(name = "content", nullable = true, length = 450) 
public String getContent() { 
    return content; 
} 

public void setContent(String content) { 
    this.content = content; 
} 

@Basic 
@Column(name = "ordered", nullable = false) 
public byte getOrdered() { 
    return ordered; 
} 

public void setOrdered(byte ordered) { 
    this.ordered = ordered; 
} 

@Basic 
@Column(name = "date", nullable = false) 
public Timestamp getDate() { 
    return date; 
} 

public void setDate(Timestamp date) { 
    this.date = date; 
} 

@Override 
public boolean equals(Object o) { 
    if (this == o) return true; 
    if (o == null || getClass() != o.getClass()) return false; 

    Advert advert = (Advert) o; 

    if (id != advert.id) return false; 
    if (profileId != advert.profileId) return false; 
    if (ordered != advert.ordered) return false; 
    if (karmaReq != null ? !karmaReq.equals(advert.karmaReq) : advert.karmaReq != null) return false; 
    if (!Arrays.equals(image, advert.image)) return false; 
    if (profileUsersUsername != null ? !profileUsersUsername.equals(advert.profileUsersUsername) : advert.profileUsersUsername != null) 
     return false; 
    if (head != null ? !head.equals(advert.head) : advert.head != null) return false; 
    if (content != null ? !content.equals(advert.content) : advert.content != null) return false; 
    if (date != null ? !date.equals(advert.date) : advert.date != null) return false; 

    return true; 
} 

@Override 
public int hashCode() { 
    int result = id; 
    result = 31 * result + (karmaReq != null ? karmaReq.hashCode() : 0); 
    result = 31 * result + Arrays.hashCode(image); 
    result = 31 * result + profileId; 
    result = 31 * result + (profileUsersUsername != null ? profileUsersUsername.hashCode() : 0); 
    result = 31 * result + (head != null ? head.hashCode() : 0); 
    result = 31 * result + (content != null ? content.hashCode() : 0); 
    result = 31 * result + (int) ordered; 
    result = 31 * result + (date != null ? date.hashCode() : 0); 
    return result; 
} 

@ManyToOne 
@JoinColumns({@JoinColumn(name = "profile_id", referencedColumnName = "id", nullable = false, insertable = false, updatable = false), @JoinColumn(name = "profile_users_username", referencedColumnName = "users_username", nullable = false, insertable = false, updatable = false)}) 
public Profile getProfile() { 
    return profile; 
} 

public void setProfile(Profile profile) { 
    this.profile = profile; 
} 

@OneToMany(mappedBy = "advertByAdvertId") 
public Collection<Comment> getCommentsById() { 
    return commentsById; 
} 

public void setCommentsById(Collection<Comment> commentsById) { 
    this.commentsById = commentsById; 
} 

@Override 
public String toString() { 
    return "Advert{" + 
      "id=" + id + 
      ", karmaReq='" + karmaReq + '\'' + 
      ", image=" + Arrays.toString(image) + 
      ", profileId=" + profileId + 
      ", profileUsersUsername='" + profileUsersUsername + '\'' + 
      ", head='" + head + '\'' + 
      ", content='" + content + '\'' + 
      ", ordered=" + ordered + 
      ", date=" + date + 
      ", profile=" + profile + 
      ", commentsById=" + commentsById + 
      '}'; 
} 

}

//我知道,如果我在为String(评论呼叫配置文件),都将是工作。

资料

@Entity 
@IdClass(ProfilePK.class) 
public class Profile { 
    private int id; 
    private String usersUsername; 
    private Integer karma; 
    private String phone; 
    private byte[] icon; 
    private Collection<Advert> adverts; 
    private Collection<Comment> comments; 
    private Collection<Message> messages; 
    private Users usersByUsersUsername; 
@Id 
@Column(name = "id", nullable = false) 
public int getId() { 
    return id; 
} 

public void setId(int id) { 
    this.id = id; 
} 

@Id 
@Column(name = "users_username", nullable = false, length = 45) 
public String getUsersUsername() { 
    return usersUsername; 
} 

public void setUsersUsername(String usersUsername) { 
    this.usersUsername = usersUsername; 
} 

@Basic 
@Column(name = "karma", nullable = true) 
public Integer getKarma() { 
    return karma; 
} 

public void setKarma(Integer karma) { 
    this.karma = karma; 
} 

@Basic 
@Column(name = "phone", nullable = true, length = 15) 
public String getPhone() { 
    return phone; 
} 

public void setPhone(String phone) { 
    this.phone = phone; 
} 

@Basic 
@Column(name = "icon", nullable = true) 
public byte[] getIcon() { 
    return icon; 
} 

public void setIcon(byte[] icon) { 
    this.icon = icon; 
} 

@Override 
public boolean equals(Object o) { 
    if (this == o) return true; 
    if (o == null || getClass() != o.getClass()) return false; 

    Profile profile = (Profile) o; 

    if (id != profile.id) return false; 
    if (usersUsername != null ? !usersUsername.equals(profile.usersUsername) : profile.usersUsername != null) 
     return false; 
    if (karma != null ? !karma.equals(profile.karma) : profile.karma != null) return false; 
    if (phone != null ? !phone.equals(profile.phone) : profile.phone != null) return false; 
    if (!Arrays.equals(icon, profile.icon)) return false; 

    return true; 
} 

@Override 
public int hashCode() { 
    int result = id; 
    result = 31 * result + (usersUsername != null ? usersUsername.hashCode() : 0); 
    result = 31 * result + (karma != null ? karma.hashCode() : 0); 
    result = 31 * result + (phone != null ? phone.hashCode() : 0); 
    result = 31 * result + Arrays.hashCode(icon); 
    return result; 
} 

@OneToMany(mappedBy = "profile") 
public Collection<Advert> getAdverts() { 
    return adverts; 
} 

public void setAdverts(Collection<Advert> adverts) { 
    this.adverts = adverts; 
} 

@OneToMany(mappedBy = "profile") 
public Collection<Comment> getComments() { 
    return comments; 
} 

public void setComments(Collection<Comment> comments) { 
    this.comments = comments; 
} 

@OneToMany(mappedBy = "profile") 
public Collection<Message> getMessages() { 
    return messages; 
} 

public void setMessages(Collection<Message> messages) { 
    this.messages = messages; 
} 

@ManyToOne 
@JoinColumn(name = "users_username", referencedColumnName = "username", nullable = false, insertable = false, updatable = false) 
public Users getUsersByUsersUsername() { 
    return usersByUsersUsername; 
} 

public void setUsersByUsersUsername(Users usersByUsersUsername) { 
    this.usersByUsersUsername = usersByUsersUsername; 
} 

@Override 
public String toString() { 
    return "Profile{" + 
      "id=" + id + 
      ", usersUsername='" + usersUsername + '\'' + 
      ", karma=" + karma + 
      ", phone='" + phone + '\'' + 
      ", icon=" + Arrays.toString(icon) + 
      ", adverts=" + adverts + 
      ", comments=" + comments + 
      ", messages=" + messages + 
      ", usersByUsersUsername=" + usersByUsersUsername + 
      '}'; 
} 

}

ORM

AdvertDAO

/** 
* DAO interface responsible for operation with Advertising. 
* <p> 
* Created by Novik Igor on 09.02.2017. 
*/ 
public interface AdvertDAO { 

    /** 
    * Method returned list of Advert's from the DB. 
    * 
    * @return list of Advertising's. 
    */ 
    List<Advert> findAll(); 

    /** 
    * Method returned list of Advert from the DB according ID. 
    * 
    * @param head id of the Advert; 
    * @return Advertising according id. 
    */ 
    Advert findByHead(String head); 
} 

AdvertDAORepository

/** 
* SpringData AdvertDAO repository. 
* 
* Created by Novik Igor on 10.02.2017. 
*/ 
public interface AdvertDAORepository extends CrudRepository<Advert,Integer> { 

    List<Advert> findByHead(String head); 

} 

服务的数据春/ JPA - AdvertDAOImpl

/** 
* Repository bean that implements JPA DAO Advert interfaces responsible for operation with Advertising from DB. 
* <p> 
* Created by nolik on 10.02.17. 
*/ 

@Service("jpaAdvertDAO") 
@Repository 
@Transactional 
public class AdvertDAOImpl implements AdvertDAO { 

    @Autowired 
    private AdvertDAORepository advertDAORepository; 

    @Override 
    public List<Advert> findAll() { 

     return Lists.newArrayList(advertDAORepository.findAll()); 
    } 

    @Override 
    public Advert findByHead(String head) { 

     return (Advert) advertDAORepository.findByHead(head); 
    } 
} 

测试MVC控制器:

@Controller 
public class TestController { 

    @Autowired 
    AdvertDAO jpaAdvertDAO; 
    @Autowired 
    CommentDAO jpaCommentDAO; 

    @RequestMapping(value = "/testCall", method = RequestMethod.GET) 
    public ModelAndView readCookieExample() { 

     System.out.println(" Test console"); 
     return new ModelAndView("/error/errorpage"); 

    } 

    @RequestMapping(value = "/jpaFindAllAdvert", method = RequestMethod.GET) 
    public ModelAndView jpaFindAllAdvert() { 
     System.out.println("ORMController ormFindAllUsers is called"); 
     List<Advert> adverts = jpaAdvertDAO.findAll(); 
     return new ModelAndView("/error/test", "resultObject", adverts); 

    } 

    @RequestMapping(value = "/jpaFindAllComments", method = RequestMethod.GET) 
    public ModelAndView jpaFindAllComments() { 
     System.out.println("ORMController FindAllComments is called"); 
     List<Comment> comments = jpaCommentDAO.findAll(); 
     return new ModelAndView("/error/test", "resultObject", comments); 

    } 
} 

简单的JSP用于显示调用的结果 “/ jpaFindAllAdvert”

<%@ page contentType="text/html;charset=UTF-8" language="java" %> 
 
<html> 
 
<head> 
 
    <title>Test</title> 
 
</head> 
 
<body> 
 
<%--<a href="${adverts}" class="list-group-item">Find All Adverts</a>--%> 
 

 
${resultObject} 
 
</body> 
 
</html>

首先,我面临着一个异常:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: by.GetItFree.entities.Profile.adverts, could not initialize proxy - no Session 
org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:563) 
org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:205) 
org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:542) 
org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:133) 
org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:509) 
java.lang.String.valueOf(String.java:2994) 
java.lang.StringBuilder.append(StringBuilder.java:131) 
by.GetItFree.entities.Profile.toString(Profile.java:144) 
java.lang.String.valueOf(String.java:2994) 
java.lang.StringBuilder.append(StringBuilder.java:131) 
by.GetItFree.entities.Advert.toString(Advert.java:174) 
java.lang.String.valueOf(String.java:2994) 
java.lang.StringBuilder.append(StringBuilder.java:131) 
java.util.AbstractCollection.toString(AbstractCollection.java:462) 
org.apache.el.lang.ELSupport.coerceToString(ELSupport.java:497) 
org.apache.el.lang.ELSupport.coerceToType(ELSupport.java:529) 
org.apache.el.ExpressionFactoryImpl.coerceToType(ExpressionFactoryImpl.java:47) 
javax.el.ELContext.convertToType(ELContext.java:304) 
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:186) 
org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:944) 
org.apache.jsp.WEB_002dINF.view.error.test_jsp._jspService(test_jsp.java:118) 
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70) 
javax.servlet.http.HttpServlet.service(HttpServlet.java:729) 
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443) 
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385) 
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329) 
javax.servlet.http.HttpServlet.service(HttpServlet.java:729) 
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) 
org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:168) 
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303) 
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1271) 
org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037) 
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980) 
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) 
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) 
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) 
javax.servlet.http.HttpServlet.service(HttpServlet.java:622) 
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) 
javax.servlet.http.HttpServlet.service(HttpServlet.java:729) 
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) 

我谷歌,它的H的Leazy初始化J的N + 1 SQL问题的结果与@ManyToONe的关系。

我使用的方法与添加修复: <prop key="hibernate.enable_lazy_load_no_trans">true</prop> 为 “JPA属性”

这一点,我面临着后:StackOverflow上exeption:

java.lang.StackOverflowError 
java.util.AbstractCollection.toString(AbstractCollection.java:454) 
org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) 
java.lang.String.valueOf(String.java:2994) 
java.lang.StringBuilder.append(StringBuilder.java:131) 
by.GetItFree.entities.Profile.toString(Profile.java:144) 
java.lang.String.valueOf(String.java:2994) 
java.lang.StringBuilder.append(StringBuilder.java:131) 
by.GetItFree.entities.Advert.toString(Advert.java:174) 
java.lang.String.valueOf(String.java:2994) 
java.lang.StringBuilder.append(StringBuilder.java:131) 
java.util.AbstractCollection.toString(AbstractCollection.java:462) 
org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:510) 
java.lang.String.valueOf(String.java:2994) 
java.lang.StringBuilder.append(StringBuilder.java:131) 
by.GetItFree.entities.Profile.toString(Profile.java:144) 
java.lang.String.valueOf(String.java:2994) 
java.lang.StringBuilder.append(StringBuilder.java:131) 
by.GetItFree.entities.Advert.toString(Advert.java:174) 

等 - 很长列表

在Tocat登录最后一种情况 - 我看到大的上市JPQL /或HSQL我不舒服:

RMController ormFindAllUsers is called 
Hibernate: select advert0_.id as id1_1_, advert0_.content as content2_1_, advert0_.date as date3_1_, advert0_.head as head4_1_, advert0_.image as image5_1_, advert0_.karmaReq as karmaReq6_1_, advert0_.ordered as ordered7_1_, advert0_.profile_users_username as profile_9_1_, advert0_.profile_id as profile_8_1_ from Advert advert0_ 
Hibernate: select profile0_.users_username as users_us1_5_0_, profile0_.id as id2_5_0_, profile0_.icon as icon3_5_0_, profile0_.karma as karma4_5_0_, profile0_.phone as phone5_5_0_, users1_.username as username1_6_1_, users1_.enabled as enabled2_6_1_, users1_.password as password3_6_1_ from Profile profile0_ inner join Users users1_ on profile0_.users_username=users1_.username where profile0_.users_username=? and profile0_.id=? 
Hibernate: select adverts0_.profile_users_username as profile_9_1_0_, adverts0_.profile_id as profile_8_1_0_, adverts0_.id as id1_1_0_, adverts0_.id as id1_1_1_, adverts0_.content as content2_1_1_, adverts0_.date as date3_1_1_, adverts0_.head as head4_1_1_, adverts0_.image as image5_1_1_, adverts0_.karmaReq as karmaReq6_1_1_, adverts0_.ordered as ordered7_1_1_, adverts0_.profile_users_username as profile_9_1_1_, adverts0_.profile_id as profile_8_1_1_ from Advert adverts0_ where adverts0_.profile_users_username=? and adverts0_.profile_id=? 
Hibernate: select profile0_.users_username as users_us1_5_0_, profile0_.id as id2_5_0_, profile0_.icon as icon3_5_0_, profile0_.karma as karma4_5_0_, profile0_.phone as phone5_5_0_, users1_.username as username1_6_1_, users1_.enabled as enabled2_6_1_, users1_.password as password3_6_1_ from Profile profile0_ inner join Users users1_ on profile0_.users_username=users1_.username where profile0_.users_username=? and profile0_.id=? 

我在GitHub上progect:ProgectSourceCode

这是什么behaiviour的原因。那么解决方案是什么? Thx为您的关注和支持。

回答

0

您遇到此问题的原因是因为您认为需要的关联应在事务边界内初始化以避免LazyInitializationException。添加一个选项来加载事务以外的集合仅仅是一个bandaid,并没有真正解决你的代码的基础设计缺陷。

如果您的观点要求您加载Profile及其相关的Advert实体集合,那么您的数据访问应该专门切换该行为或查询指定您需要初始化该集合。

有很多方法可以触发此集合作为查询的一部分加载。

  • JPQL/HQL在adverts集合上使用JOIN FETCH
  • 使用Criteria API指定联合抓取
  • 使用@FetchProfile按名称切换特定的抓取策略。
  • 使用@NamedEntityGraph
+0

谢谢您的明确解释!我会尝试评估我的设计。并以建议的方式触发采集。 – Nolik

相关问题