我使用Hibernate 4和Spring 3。
我有两个实体。
图书实体
@Entity @Table(name = "book") public class Book implements Serializable { public Book() { } private static final long serialVersionUID = 1L; @Id @GeneratedValue( strategy = GenerationType.IDENTITY) private int id; @ManyToOne() @JoinColumn( name = "author_id" ) private Author author; private String name; private int pages; @Version @Column( name = "VERSION") private int version; public int getId() { return id; } public void setId(int id) { this.id = id; } public Author getAuthor() { return author; } public void setAuthor(Author author) { this.author = author; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPages() { return pages; } public void setPages(int pages) { this.pages = pages; } public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } }
和作者实体
@Entity @Table(name = "author") public class Author implements Serializable { public Author() { } private static final long serialVersionUID = 1L; @Id @GeneratedValue( strategy = GenerationType.IDENTITY) private int id; private String name; @OneToMany( mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true) private Set<Book> books = new HashSet<Book>(); public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Book> getBooks() { return books; } public void setBooks(Set<Book> books) { this.books = books; } public void addBook(Book book) { book.setAuthor(this); getBooks().add(book); } public void removeBook(Book book) { getBooks().remove(book); } }
和JSON取决于pom.xml
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.1.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-hibernate4</artifactId> <version>2.1.2</version> </dependency>
我的根上下文在这里-
<!-- Root Context: defines shared resources visible to all other web components --> <context:annotation-config/> <context:component-scan base-package="org.jar.libs.dao" /> <context:component-scan base-package="org.jar.libs.service" /> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/hibernate" p:username="root" p:password="root" /> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="annotatedClasses"> <list> <value>org.jar.libs.domain.Book</value> <value>org.jar.libs.domain.Author</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>
… servlet-context.xml
<!-- Enables the Spring MVC @Controller programming model --> <annotation-driven /> <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory --> <resources mapping="/resources/**" location="/resources/" /> <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory --> <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <beans:property name="prefix" value="/WEB-INF/views/" /> <beans:property name="suffix" value=".jsp" /> </beans:bean> <context:component-scan base-package="org.jar.libs.controller" />
控制器。
@Controller @RequestMapping (value = "books/rest") public class BookController { @Autowired private BookService bookService; // logger private static final Logger logger = LoggerFactory.getLogger(BookController.class); @SuppressWarnings("unchecked") @RequestMapping( method = RequestMethod.GET ) public @ResponseBody List<Book> getBook() { List<Book> res = bookService.findAll(); return res; } }
在我的DAO中找到findAll:
public List<Book> findAll() { Session session = sessionFactory.getCurrentSession(); List<Book> result = (List<Book>) session.createQuery("select c from Book c").list(); return result; }
在调试中,我看到该方法返回2条记录,但是Spring无法将结果转换为JSON并返回406 HTTP错误。怎么了?
我附上我在调试中看到的图像。- http://tinypic.com/view.php?pic=35kvi9i&s=6
通常,当您从事务中调用实体类的getter方法(返回关系对象)时,会得到LazyInitializationExceptions。
LazyInitializationException
如果您将实体类对象(从查询中检索)转换 为事务外的 json , 则可能是这种情况 。
我遇到了同样的问题,我在控制器中将通过hibernate获取的实体对象转换为json。由于控制器已退出事务处理(服务层的事务处理),因此在转换为json时,将调用实体类对象的getter方法,并且得到了LazyInitializationException。哪个阻碍了对象到json的转换,并且没有返回响应。
我的解决方案,试试这个:
@SuppressWarnings("unchecked") @RequestMapping( method = RequestMethod.GET ) public @ResponseBody List<Book> getBook() { List<Book> res = bookService.findAll(); for(Book book : res) { book.getAuthor().setBooks(null); } return res; }