我在Mironaut有一个简单的应用程序,有三个实体,Customer,Contact和Loans。客户与联系人和贷款有1对多的关系。我用Grails / Gorm测试它运行正常。
我有一个运行良好的DataLoader类,并创建所有实体及其关系。
/ ****** Contact.groovy ******* /
package com.gnc.demo.domain
import grails.gorm.annotation.Entity
@Entity
class Contact {
Long id
Long version
Customer customer
static belongsTo = Customer
String email
String phone
String cellPhone
String address
}
/ ****** Customer.groovy ******* /
package com.gnc.demo.domain
import grails.gorm.annotation.Entity
@Entity
class Customer {
Long id
Long version
String driverId
String name
String lastName
static hasMany = [contacts: Contact, loans: Loan]
static constraints = {
contacts nullable: true
loans nullable: true
}
static mapping = {
contacts lazy: false
loans lazy: false
}
}
/ ****** Loan.groovy ******* /
package com.gnc.demo.domain
import grails.gorm.annotation.Entity
@Entity
class Loan {
Long id
Long version
Customer customer
static belongsTo = Customer
BigDecimal ammount
long term
BigDecimal rate
}
/ ******* CustomerController.groovy ******* /
package com.gnc.demo.controllers
import com.gnc.demo.domain.Customer
import com.gnc.demo.services.ContactService
import com.gnc.demo.services.CustomerService
import com.gnc.demo.services.LoanService
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@Controller("/customer")
class CustomerController {
private static final Logger LOG = LoggerFactory.getLogger(CustomerController.class);
final CustomerService customerService
final LoanService loanService
final ContactService contactService
CustomerController(CustomerService customerService, LoanService loanService, ContactService contactService) {
this.customerService = customerService
this.loanService = loanService
this.contactService = contactService
}
@Get("/")
String index() {
return "Hola ! " + new Date()
}
@Get("/all/{offset}/{max}")
List<Customer> getCustomers(String offset, String max) {
List<Customer> customers = customerService.findAll([offset: offset, max: max])
try {
customers.each { customer ->
// LOG.info(">>> Loans :" +customer.loans.size())
customer.contacts = []
customer.loans = []
}
} catch (Exception e) {
LOG.info(">>> Error :" + e)
}
return customers
}
@Get("/{id}")
Customer getCustomers(String id) {
Customer customer = customerService.get(id)
customer?.contacts = []
customer?.loans = []
customer?.contacts = contactService.findAllByCustomer(customer)
customer?.loans = loanService.findAllByCustomer(customer)
return customer
}
}
所有代码均可在以下网址获得:https://github.com/gnpitty/com-gnc-demo
但是当我用我的浏览器在Micronaut上测试时:http://localhost:9020/customer/10
我收到此错误:
{"message":"Internal Server Error: Error encoding object
[com.gnc.demo.domain.Customer : 10] to JSON: could not initialize proxy - no
Session (through reference chain: com.gnc.demo.domain.Customer[\"contacts\"]-
>java.util.LinkedHashSet[0]->com.gnc.demo.domain.Contact[\"customer\"]-
>com.gnc.demo.domain.Customer_$$_jvst110_0[\"driverId\"])"}
正如一条评论所说,你应该确保在阅读记录时使用@Transactional或withTransaction {}。
此外,如果要引用代理元素(如Customer引用),则需要强制读取代理元素。我知道两种方法:1)对它们进行急切的获取或2)明确地解析代理。
我选择了选项2),因为我不想在不需要的时候强行提取。我只在控制器中使用它,我返回一个JSON编码的域对象。这通常只在我的REST API方法中。
例:
Loan.withTransaction {
def loan = Loan.findByXYZ()
resolveProxies(loan)
}
这会将代理转换为实际对象,因此您可以在withTransaction {}闭包之外访问它们。这通常是杰克逊将它们转换为JSON。
我使用此方法来解析列表中的任何代理或作为对另一个域对象的简单引用:
/**
* Resolves all proxies for the given domain class. This allows the domain to be used outside of an hibernate session
* if needed. This will check all fields and sub-objects for proxies.
* <p>
* <b>Note:</b> This will usually force a read of all referenced objects.
* @param object The object.
*/
def resolveProxies(Object object) {
if (object == null) {
return
}
for (property in object.class.gormPersistentEntity.persistentProperties) {
def value = object[property.name]
if (Collection.isAssignableFrom(property.type) && value) {
for (item in value) {
if (item != null) {
// Resolved any sub-objects too.
resolveProxies(item)
}
}
} else if (value instanceof HibernateProxy) {
// A simple reference, so unproxy it the GORM way.
object[property.name] = value.getClass().get(value.id)
}
}
}
随意在您需要的任何地方使用此代码。