Почему я получаю сообщение об ошибке при обращении к данным в приложении Micronaut Gorm?
У меня есть простое приложение в Mironaut с тремя объектами: клиент, контакт и кредиты. Клиент имеет отношения 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\"])"}
1 ответ
Как сказано в одном комментарии, вы должны убедиться, что @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)
}
}
}
Не стесняйтесь использовать этот код везде, где вам это нужно.