【问题标题】:Why do I receive an error when I consult data in my Micronaut Gorm application?为什么我在 Micronaut Gorm 应用程序中查阅数据时会收到错误消息?
【发布时间】:2026-01-29 03:30:01
【问题描述】:

我在 Mironaut 中有一个简单的应用程序,其中包含三个实体:客户、联系人和贷款。 客户与联系人和贷款有一对多的关系。我用 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 amount
    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\"])"}

【问题讨论】:

  • 我认为您的服务方法缺少grails.gorm.transactions.Transactional 注释。
  • 如果要将整个对象编码为json,则需要确保对象中加载了所有数据。该错误表明代理正在尝试初始化,这表明所有数据都没有提前加载

标签: grails-orm micronaut


【解决方案1】:

正如一条评论所说,您应该确保在阅读记录时使用@Transactional 或 withTransaction {}。

此外,如果您想引用代理元素(如客户引用),则需要强制读取代理元素。我知道两种方法:1) 急切地获取它们或 2) 显式解析代理。

我选择了选项 2),因为我不想在不需要时强制急切获取。我只在返回 JSON 编码域对象的控制器中使用它。这通常只是在我的 REST API 方法中。

例子:

Loan.withTransaction {
  def loan = Loan.findByXYZ()
  resolveProxies(loan)   
}

这会将代理转换为真实对象,以便您可以在 withTransaction{} 闭包之外访问它们。这通常是 Jackson 将它们转换为 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)
      }

    }
  }

您可以在任何需要的地方随意使用此代码。

【讨论】:

    最近更新 更多