简体   繁体   English

如何修复此错误:无法写入 JSON:未能延迟初始化角色集合:com.cashregister.demo.model

[英]How to fix this error: Could not write JSON: failed to lazily initialize a collection of role: com.cashregister.demo.model

I'm getting stuck when trying to create a Transaction object and then return a list of transactions.我在尝试创建事务 object 然后返回事务列表时遇到问题。 The error I'm getting when calling http://localhost:8080/transactions is:我在调用 http://localhost:8080/transactions 时遇到的错误是:

{
  "timestamp": "2020-08-13T14:24:11.113+0000",
  "status": 500,
  "error": "Internal Server Error",
  "message": "Could not write JSON: failed to lazily initialize a collection of role: com.cashregister.demo.model.Transaction.items, could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.cashregister.demo.model.Transaction.items, could not initialize proxy - no Session (through reference chain: java.util.HashMap[\"transactions\"]->java.util.ArrayList[0]->com.cashregister.demo.model.Transaction[\"items\"])",
  "path": "/transactions"
}

I've been searching for solutions, but none seem to get rid of the error and return.我一直在寻找解决方案,但似乎没有一个能摆脱错误并返回。 Can someone please help resolve this issue so that the response when getting all transactions is returned in the format:有人可以帮助解决此问题,以便在获取所有交易时以以下格式返回响应:

{
  "transaction": {
    "id": 1,
    "total": 46.44,
    "customer": {
      "id": 1,
      "phoneNumber": "9416970394",
      "lastName": "Weber",
      "loyaltyNumber": "2484801419"
    },
    "items": [
     // list of each item in the transaction
    ]

Here is my code:这是我的代码:

Item.java Model Item.java Model

@Entity
public class Item {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "transaction_id", nullable = false)
    private Transaction transaction;

    @NotNull
    private double total;

    @NotNull
    @OneToOne
    private Product product;

    @NotNull
    private int quantity;

// Getters and setters
}

Transaction.java Model Transaction.java Model

@Entity
public class Transaction {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private double total;

    @ManyToOne
    private Customer customer;

    @OneToMany(mappedBy = "transaction", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private List<Item> items = new ArrayList<>();

// Getters and setters
}

TransactionDaoImpl.java for DB Access TransactionDaoImpl.java 用于数据库访问

@Repository
public class TransactionDaoImpl implements TransactionDao {
    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public List<Transaction> findAll() {
        Session session = sessionFactory.openSession();
        List<Transaction> transactions = session.createCriteria(Transaction.class).list();
        session.close();
        return transactions;
    }

    @Override
    public Transaction findById(Long id) {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.get(Transaction.class, id);
        session.close();
        return transaction;
    }

    @Override
    public Long save(Transaction transaction) {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        session.saveOrUpdate(transaction);
        session.getTransaction().commit();
        session.close();
        return transaction.getId();
    }
}

TransctionController.java TransctionController.java

@RestController
@RequestMapping("/transactions")
public class TransactionController {
    @Autowired
    private TransactionService transactionService;

    @Autowired
    private CustomerService customerService;

    @Autowired
    private ProductService productService;

    @Autowired
    private ItemService itemService;

    @RequestMapping(value = "")
    public Map<String, List<Transaction>> listTransactions() {
        Map<String, List<Transaction>> response = new HashMap<>();
        List<Transaction> transactions = transactionService.findAll();
        response.put("transactions", transactions);
        return response;
    }

    @RequestMapping(value = "{transaction_id}")
    public Map<String, Transaction> findTransactionById(@PathVariable Long transaction_id) {
        Map<String, Transaction> response = new HashMap<>();
        Transaction transaction = transactionService.findById(transaction_id);
        response.put("transaction", transaction);
        return response;
    }

    @RequestMapping(value = "", method = RequestMethod.POST)
    public Map<String, Transaction> createTransaction(@RequestBody List<Product> products, @RequestParam("user_id") Long customerId) {
        Map<String, Transaction> response = new HashMap<>();

        Customer customer = customerService.findById(customerId);

        // Get unique skus to get the product objects from the database (source of truth).
        Set<String> skus = new HashSet<>();
        for (final Product product : products) {
            skus.add(product.getSku());
        }
        List<String> skusStrings = new ArrayList<String>();
        for(String sku : skus) {
            skusStrings.add(sku);
        }
        List<Product> productsFromDb = productService.findBySkus(skusStrings);

        // Loop through products in payload to calculate quantity.
        List<Item> items = new ArrayList<>();
        Map<String, Integer> productQuantity = new HashMap<>();
        for(Product product : products) {
            if(productQuantity.containsKey(product.getSku())) {
                productQuantity.put(product.getSku(), productQuantity.get(product.getSku()) + 1);
            } else {
                productQuantity.put(product.getSku(), 1);
            }
        }

        // Calculate total cost
        double total = 0;
        for(Product product : productsFromDb) {
            if(!customer.getLoyaltyNumber().isEmpty()) {
                total += product.getDiscountPrice() * productQuantity.get(product.getSku());
            } else {
                total += product.getDefaultPrice() * productQuantity.get(product.getSku());
            }
        }

        // Calculate item total and append to Items list
        Map<String, Double> productTotal = new HashMap<>();
        for(Product product : productsFromDb) {
            if(!customer.getLoyaltyNumber().isEmpty()) {
                productTotal.put(product.getSku(), productQuantity.get(product.getSku()) * product.getDiscountPrice());
            } else {
                productTotal.put(product.getSku(), productQuantity.get(product.getSku()) * product.getDefaultPrice());
            }
        }

        Transaction t = new Transaction();
        t.setTotal(total);
        t.setCustomer(customer);
        t.setItems(items);

        Long transactionId = transactionService.save(t);

        for(Product product : productsFromDb) {
            Item item = new Item(t, productTotal.get(product.getSku()), product, productQuantity.get(product.getSku()));
            itemService.save(item);
        }

        Transaction transaction = transactionService.findById(transactionId);

        response.put("transaction", transaction);
        return response;
    }
}

Your Transaction entity has relation to Item entity.您的 Transaction 实体与 Item 实体有关系。 So in your controller you don't have any database session for fetching Items.因此,在您的 controller 中,您没有任何数据库 session 用于获取项目。 You must put all your code in Service layer and then put @Transactional on it.您必须将所有代码放在服务层中,然后将@Transactional放在上面。 Short and bad response is set the spring.jpa.open-in-view property to true.spring.jpa.open-in-view属性设置为 true。

The issue here is Lazy Loading by Hibernate .这里的问题是Hibernate 的延迟加载 This is actually JPA spec and implemented by Hibernate.这实际上是 JPA 规范,由 Hibernate 实现。 the main concept is not to load up all association, giving a performance boost.主要概念是不加载所有关联,从而提高性能。

Why you facing this issue:为什么你面临这个问题:

  • You have defined the FETCH strategy as LAZY on both sides (Here, Relationship from Transaction to Item matters since you fetching Transaction)您已将 FETCH 策略定义为双方的 LAZY(此处,从交易到项目的关系很重要,因为您获取交易)
  • TransactionDaoImpl#findAll() method opens a Session and fetched the records. TransactionDaoImpl#findAll() 方法打开 Session 并获取记录。 Hibernate, according to the Lazy Load concept, does not fetch the associated Items from the DB, instead, creates Proxies. Hibernate 根据延迟加载的概念,不会从数据库中获取关联的项目,而是创建代理。
  • Since these are Proxy Objects, the HttpMessageConverter cannot convert those objects to JSON representation.由于这些是代理对象,HttpMessageConverter 无法将这些对象转换为 JSON 表示。

How to resolve the issue:如何解决问题:

  1. You have to call a get accessor on the item relationship.您必须在项目关系上调用 get 访问器。
   @Transactional // This is required since you are not using Spring data Jpa Repository abstractions. Or else you can explicitly define the transaction boundary inside the method by opening the transaction and closing it.
   @Override
    public List<Transaction> findAll() {
        Session session = sessionFactory.openSession();
        List<Transaction> transactions = session.createCriteria(Transaction.class).list();
        transactions.foreach(Transaction::getItems);
        session.close();
        return transactions;
    }

But this will give rise to a N+1 issue .但这会引起N+1 的问题

Mainly.主要是。 for each transaction, N number of queries will be fired for each transaction (N is the number of Items for one transaction) - so K*N number of queries will be fired.对于每个事务,每个事务将触发 N 个查询(N 是一个事务的项目数) - 因此将触发 K*N 个查询。

  1. To resolve the N+1 issue, you can go with a Native query using Join to fetch all the data with one single query.要解决 N+1 问题,您可以 go 与 Native 查询一起使用 Join 以通过一个查询获取所有数据。 Since a JOIN, still performance will be slow, but much better than 1st option.由于加入,性能仍然会很慢,但比第一个选项好得多。 Or you can with Hibernate Annotation or JPQL/HQL JOIN FETCH , which essentially creates the JOIN for you.或者您可以使用Hibernate Annotation或 JPQL/HQL JOIN FETCH ,这实际上为您创建了 JOIN。

  2. Another Option to load the data without N+1 query is @EntityGraphy .在没有 N+1 查询的情况下加载数据的另一个选项是@EntityGraphy I have not used it.我没用过。 But you can read more about it here但是你可以在这里阅读更多关于它的信息

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 无法编写JSON:无法延迟初始化角色集合:com.managem.model.Region.pays,无法初始化代理-没有会话 - Could not write JSON: failed to lazily initialize a collection of role: com.managem.model.Region.pays, could not initialize proxy - no Session 无法写入 JSON:未能延迟初始化角色集合 - Could not write JSON: failed to lazily initialize a collection of role 无法编写JSON:无法延迟初始化集合 - Could not write JSON: failed to lazily initialize a collection 延迟获取具有多个关系的实体:无法写入 JSON:未能延迟初始化角色集合 - Lazily fetching entity with multiple relations: Could not write JSON: failed to lazily initialize a collection of role @ManyToMany in Spring Boot(无法延迟初始化角色集合:com.example.demo.Movie.actors,无法初始化代理 - 无会话) - @ManyToMany in Spring Boot (failed to lazily initialize a collection of role: com.example.demo.Movie.actors, could not initialize proxy - no Session) 无法写入内容:未能延迟初始化角色集合 - Could not write content: failed to lazily initialize a collection of role 无法编写内容:无法使用OpenEntityManagerInViewFilter延迟初始化角色集合 - Could not write content: failed to lazily initialize a collection of role using OpenEntityManagerInViewFilter 使用 springboot 和 hibernate 搜索“无法编写 JSON:无法延迟初始化角色集合” - "Could not write JSON: failed to lazily initialize a collection of role" with springboot and hibernate search 无法修复延迟初始化角色集合失败:无法初始化代理 - 无会话 - Can't fix failed to lazily initialize a collection of role: could not initialize proxy - no Session 无法延迟初始化角色集合无法初始化代理-无会话 - Failed to lazily initialize a collection of role could not initialize proxy - no Session
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM