我正在构建使用并返回JSON的RESTful Web服务。当我尝试通过服务层从数据库中获取ESRBRating对象时,遇到了以下堆栈跟踪。但是,当我将Spring Data JPA存储库直接注入到控制器,并使用该ID通过ID获取ESRBRating时,它可以正常工作。但是,当通过服务层进行调用时,它不起作用。我在下面提供了堆栈跟踪和代码。有人可以向我解释为什么通过服务层而不是直接通过Spring Data JPA存储库时会发生这种情况吗?我该如何解决这个问题?
堆栈跟踪
Caused by: org.hibernate.LazyInitializationException: could not initialize proxy - no Session at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286) at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) at net.jkratz.igdb.model.ESRBRating_$$_jvst319_0.getId(ESRBRating_$$_jvst319_0.java) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:466) at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:639) ... 76 more
控制者
@RestController @RequestMapping(produces = "application/json", value="/esrbrating") public class ESRBRatingController { @Inject ESRBRatingService esrbRatingService; @Inject ESRBRatingRepository esrbRatingRepository; private Logger logger = LoggerFactory.getLogger(getClass()); /** * Returns all ESRB ratings in database * * @return List of ESRBRating objects * @see ESRBRating */ @RequestMapping(value = {"","/"}, method = RequestMethod.GET) public ResponseEntity<List<ESRBRating>> getESRBRatings() { List<ESRBRating> esrbRatings = esrbRatingService.getESRBRatings(); return new ResponseEntity<>(esrbRatings, HttpStatus.OK); } /** * Returns a single ESRB rating object if exists, otherwise returns HTTP status code 404 * * @param id ID of the ESRB Rating * @return ESRB Rating when found * @see ESRBRating */ @RequestMapping(value = "/{id}", method = RequestMethod.GET) public ResponseEntity<?> getUser(@PathVariable("id") Long id) { logger.debug("Attempting to fetch ESRB rating with ID: {}", id); ESRBRating esrbRating = esrbRatingService.getESRBRating(id); //ESRBRating esrbRating = esrbRatingRepository.findOne(id); if (esrbRating == null) { return new ResponseEntity<>("ESRB Rating not found", HttpStatus.NOT_FOUND); } else { return new ResponseEntity<>(esrbRating, HttpStatus.OK); } } }
服务
@Service @Transactional public class ESRBRatingServiceImpl implements ESRBRatingService { @Value("#{paging.games.maxPageSize}") private static int DEFAULT_PAGE_SIZE; @Value("#{paging.games.maxPageSize}") private static int MAX_PAGE_SIZE; @Inject private ESRBRatingRepository esrbRatingRepository; @Override public List<ESRBRating> getESRBRatings() { List<ESRBRating> ratings = esrbRatingRepository.findAll(); return ratings; } @Override public ESRBRating getESRBRating(Long id) { return esrbRatingRepository.getOne(id); } @Override public ESRBRating saveESRBRating(ESRBRating esrbRating) { return esrbRatingRepository.saveAndFlush(esrbRating); } @Override public boolean deleteESRBRating(Long id) { esrbRatingRepository.delete(id); return true; } }
资料库
package net.jkratz.igdb.repository; import net.jkratz.igdb.model.ESRBRating; import org.springframework.data.jpa.repository.JpaRepository; public interface ESRBRatingRepository extends JpaRepository<ESRBRating, Long> { }
模型
@Entity @Table(name = "esrb_rating", schema = "igdb") @JsonIgnoreProperties(ignoreUnknown = true) public class ESRBRating implements Serializable { private static final long serialVersionUID = 1L; @Id @NotNull @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) private Long id; @NotNull @Size(min = 1, max = 255) @Column(name = "title", nullable = false, length = 255) private String title; @Size(max = 65535) @Column(length = 65535) private String description; @OneToMany(cascade = CascadeType.ALL, mappedBy = "esrbRating") private List<Game> games; public ESRBRating() { } public ESRBRating(Long id, String title, String description) { this.id = id; this.title = title; this.description = description; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ESRBRating that = (ESRBRating) o; return Objects.equal(this.id, that.id) && Objects.equal(this.title, that.title) && Objects.equal(this.description, that.description); } @Override public int hashCode() { return Objects.hashCode(id, title, description); } @Override public String toString() { return Objects.toStringHelper(this) .add("id", id) .add("title", title) .add("description", description) .toString(); } }
此控制器代码可以正常工作,可以直接遍历存储库。
@RestController @RequestMapping(produces = "application/json", value="/esrbrating") public class ESRBRatingController { @Inject ESRBRatingService esrbRatingService; @Inject ESRBRatingRepository esrbRatingRepository; private Logger logger = LoggerFactory.getLogger(getClass()); /** * Returns all ESRB ratings in database * * @return List of ESRBRating objects * @see ESRBRating */ @RequestMapping(value = {"","/"}, method = RequestMethod.GET) public ResponseEntity<List<ESRBRating>> getESRBRatings() { List<ESRBRating> esrbRatings = esrbRatingService.getESRBRatings(); return new ResponseEntity<>(esrbRatings, HttpStatus.OK); } /** * Returns a single ESRB rating object if exists, otherwise returns HTTP status code 404 * * @param id ID of the ESRB Rating * @return ESRB Rating when found * @see ESRBRating */ @RequestMapping(value = "/{id}", method = RequestMethod.GET) public ResponseEntity<?> getUser(@PathVariable("id") Long id) { logger.debug("Attempting to fetch ESRB rating with ID: {}", id); ESRBRating esrbRating = esrbRatingRepository.findOne(id); if (esrbRating == null) { return new ResponseEntity<>("ESRB Rating not found", HttpStatus.NOT_FOUND); } else { return new ResponseEntity<>(esrbRating, HttpStatus.OK); } }
}
更新:
我遵循Randall Harleigh的建议,并使用@JsonIgnore设置了反向集合。但是现在我得到了完全不同的堆栈跟踪。现在看来Jackson / Spring不知道如何序列化ESRBRating。关于这个有什么提示吗?
org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: net.jkratz.igdb.model.ESRBRating_$$_jvstb5c_0["handler"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: net.jkratz.igdb.model.ESRBRating_$$_jvstb5c_0["handler"]) at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:238) at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:208) at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:158) at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:138) at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:71) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:122) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:781) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:721) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857) at javax.servlet.http.HttpServlet.service(HttpServlet.java:620) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842) at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:155) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745) Caused by: com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: net.jkratz.igdb.model.ESRBRating_$$_jvstb5c_0["handler"]) at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59) at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:26) at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:505) at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:639) at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:152) at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:114) at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:1887) at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:231) ... 72 more
更新2:
我最终将@Proxy(lazy = false)放在ESRBRating类上,现在可以正常使用了。但是我很好奇这会对性能产生什么样的影响?
@Entity @Table(name = "esrb_rating", schema = "igdb") @JsonIgnoreProperties(ignoreUnknown = false) @Proxy(lazy = false) public class ESRBRating implements Serializable { private static final long serialVersionUID = 1L; @Id @NotNull @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) private Long id; @NotNull @Size(min = 1, max = 255) @Column(name = "title", nullable = false, length = 255) private String title; @Size(max = 65535) @Column(length = 65535) private String description; @OneToMany(cascade = CascadeType.ALL, mappedBy = "esrbRating") private List<Game> games; public ESRBRating() { } public ESRBRating(Long id, String title, String description) { this.id = id; this.title = title; this.description = description; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @JsonIgnore public List<Game> getGames() { return games; } public void setGames(List<Game> games) { this.games = games; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ESRBRating that = (ESRBRating) o; return Objects.equal(this.id, that.id) && Objects.equal(this.title, that.title) && Objects.equal(this.description, that.description); } @Override public int hashCode() { return Objects.hashCode(id, title, description); } @Override public String toString() { return Objects.toStringHelper(this) .add("id", id) .add("title", title) .add("description", description) .toString(); } }
根据要求,这里是游戏类
@Entity @Table(name = "game", schema = "igdb", indexes = {@Index(name = "idx_game_title", columnList = ("title"), unique = false), @Index(name = "idx_game_developer", columnList = ("developer"), unique = false), @Index(name = "idx_game_publisher", columnList = ("publisher"), unique = false)}) @JsonIgnoreProperties(ignoreUnknown = true) public class Game implements Serializable { private static final long serialVersionUID = 1L; @Id @NotNull @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) private Long id; @NotNull @Size(min = 1, max = 255) @Column(name = "title", nullable = false, length = 255) private String title; @Size(max = 65535) @Column(name = "description", length = 65535) private String description; @Size(max = 255) @Column(name = "developer", length = 255) private String developer; @Size(max = 255) @Column(name = "publisher", length = 255) private String publisher; @NotNull @Size(max = 4) @Column(name = "players", nullable = false) private short players; @NotNull @Column(name = "cooperative", nullable = false) private boolean cooperative; @NotNull @Column(name = "release_date", nullable = false) @Temporal(TemporalType.DATE) private Date releaseDate; @Size(max = 255) @Column(name = "image", length = 255) private String image; @JoinColumn(name = "esrb_rating_id", referencedColumnName = "id", nullable = false) @ManyToOne(optional = false, fetch = FetchType.EAGER) private ESRBRating esrbRating; @OneToMany(cascade = CascadeType.ALL, mappedBy = "game") private List<GamePlatformMap> gamePlatformMap; @OneToMany(cascade = CascadeType.ALL, mappedBy = "game") private List<GameGenreMap> gameGenreMap; public Game() { } public Game(Long id, String title, String description, String developer, String publisher, short players, boolean cooperative, Date releaseDate, String image, ESRBRating esrbRating) { super(); this.id = id; this.title = title; this.description = description; this.developer = developer; this.publisher = publisher; this.players = players; this.cooperative = cooperative; this.releaseDate = releaseDate; this.image = image; this.esrbRating = esrbRating; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getDeveloper() { return developer; } public void setDeveloper(String developer) { this.developer = developer; } public String getPublisher() { return publisher; } public void setPublisher(String publisher) { this.publisher = publisher; } public short getPlayers() { return players; } public void setPlayers(short players) { this.players = players; } public boolean isCooperative() { return cooperative; } public void setCooperative(boolean cooperative) { this.cooperative = cooperative; } public Date getReleaseDate() { return releaseDate; } public void setReleaseDate(Date releaseDate) { this.releaseDate = releaseDate; } public String getImage() { return image; } public void setImage(String image) { this.image = image; } public ESRBRating getEsrbRating() { return esrbRating; } public void setEsrbRating(ESRBRating esrbRating) { this.esrbRating = esrbRating; } @JsonIgnore public List<GamePlatformMap> getGamePlatformMap() { return gamePlatformMap; } public void setGamePlatformMap(List<GamePlatformMap> gamePlatformMap) { this.gamePlatformMap = gamePlatformMap; } @JsonIgnore public List<GameGenreMap> getGameGenreMap() { return gameGenreMap; } public void setGameGenreMap(List<GameGenreMap> gameGenreMap) { this.gameGenreMap = gameGenreMap; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Game that = (Game) o; return Objects.equal(this.id, that.id) && Objects.equal(this.title, that.title) && Objects.equal(this.description, that.description) && Objects.equal(this.developer, that.developer) && Objects.equal(this.publisher, that.publisher) && Objects.equal(this.players, that.players) && Objects.equal(this.cooperative, that.cooperative) && Objects.equal(this.releaseDate, that.releaseDate) && Objects.equal(this.image, that.image) && Objects.equal(this.esrbRating, that.esrbRating); } @Override public int hashCode() { return Objects.hashCode(id, title, description, developer, publisher, players, cooperative, releaseDate, image, esrbRating); } @Override public String toString() { return Objects.toStringHelper(this) .add("id", id) .add("title", title) .add("description", description) .add("developer", developer) .add("publisher", publisher) .add("players", players) .add("cooperative", cooperative) .add("releaseDate", releaseDate) .add("image", image) .add("esrbRating", esrbRating) .toString(); } }
This typically happens when you are returning an object via @Responsebody (or in your case response body by way of @RestController) and an object is being serialized but has children in a LAZY collection that have not been referenced. By the time you are in your controller there is no longer a transaction active that will facilitate them being fetched (like the one your started in your @Service). You can either make your fetch strategy EAGER, bring in your collections by reference while still in your transaction or make your LAZY collections JSON Transient.