You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java EE运行时持久化新对象的正确实现方法

Hey there! Let's break down how to properly persist data after your Java EE app has started, while sticking to the MVC pattern and fixing your current issues.

Core Approach: Correct Way to Persist Data at Runtime in Java EE

First, a key rule to follow: Controllers (like your UploadController) should NOT handle persistence logic directly. In MVC, controllers exist to receive requests, validate input, and delegate business/persistence work to dedicated layers. Persistence should live in DAOs (Data Access Objects) or EJBs, with transactions managed by the Java EE container—this is the standard, maintainable approach.

Why Your Previous Solutions Failed

1. TransactionRequiredException in Solution 1

Your UploadController is likely a @RequestScoped or @ViewScoped bean (common for JSF controllers). The @PersistenceContext-injected EntityManager is transaction-scoped by default—meaning it only works when a valid transaction context exists. When you tried to call em.persist() directly in the controller, there was no active transaction, hence the exception.

Also, reusing the InitBean's persistence context is a bad idea: singletons have a completely different lifecycle than request-scoped controllers, and mixing scopes like this breaks Java EE's container-managed rules.

2. Static Method in Solution 2

Static methods exist outside the container-managed bean lifecycle. The EntityManager injection only works for instantiated beans managed by the container—so your static method would never get a valid EntityManager instance. This approach also breaks object-oriented design principles, so you were right to feel it's "un规范" (unconventional).

Step-by-Step Fix

1. Refactor Your ProductDAO to Use a Stateless EJB

Your existing ProductDAO uses @Model (equivalent to @RequestScoped + @Named), but @Stateless EJBs are the standard for persistence/business logic in Java EE. They automatically handle container-managed transactions (CMT), so you don't have to manually manage transactions.

Here's how to update your ProductDAO (completing the code you shared):

package at.technikum.mic16.prj.dao;

import at.technikum.mic16.prj.entity.Product;
import java.io.Serializable;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless // Critical: Container manages transactions and instance pooling
public class ProductDAO implements Serializable {

    @PersistenceContext(unitName = "daisy-persunit")
    private EntityManager em;

    // Your existing find method
    public Product findByID(Long id) {
        return em.find(Product.class, id);
    }

    // Add method to persist new Product
    public void persistProduct(Product product) {
        em.persist(product); // Transaction is automatically created by the container
    }

    // Optional: Add method for updating existing Products
    public Product updateProduct(Product product) {
        return em.merge(product);
    }

    // Add your unfinished find all method here if needed
}

@Stateless methods default to @TransactionAttribute(REQUIRED)—meaning the container starts a transaction if none exists, and commits it when the method completes (no exceptions = commit, exception = rollback).

2. Inject the ProductDAO into Your UploadController

Your controller should remain a request-scoped CDI bean (for MVC separation), and you'll inject the ProductDAO EJB to handle persistence. Example:

import at.technikum.mic16.prj.dao.ProductDAO;
import at.technikum.mic16.prj.entity.Product;
import javax.inject.Named;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;

@Named // Makes the bean accessible to JSF views
@RequestScoped // Correct scope for a controller: one instance per HTTP request
public class UploadController {

    @Inject // Container injects a valid ProductDAO instance from its pool
    private ProductDAO productDAO;

    // Method to handle your XML upload and parsed Product
    public String handleXmlUpload(Product parsedProduct) {
        // Optional: Add validation here (e.g., check for duplicate product names)
        productDAO.persistProduct(parsedProduct); // Persist with container-managed transaction
        return "success?faces-redirect=true"; // Redirect to success page
    }
}

Now when handleXmlUpload is called, the container will automatically manage the transaction for productDAO.persistProduct(), so you won't get the TransactionRequiredException anymore.

3. Quick Scope Recap

  • UploadController: @RequestScoped is ideal—each request gets a fresh instance, which aligns with MVC controller responsibilities.
  • ProductDAO: @Stateless lets the container manage instance pooling and transactions, so you don't have to worry about scope details.
  • EntityManager: Injected via @PersistenceContext in a @Stateless bean, it's transaction-scoped—one instance per active transaction, managed entirely by the container.

Bonus Best Practices

  • Keep controllers thin: Never put business/persistence logic in controllers—this keeps your code modular, testable, and aligned with MVC.
  • Use Bean Validation: Add validation annotations to your Product entity to ensure data integrity before persistence:
    @Column(unique = true, nullable = false)
    @NotNull(message = "Product name cannot be empty")
    private String name;
    
    @Column(nullable = false)
    @Min(value = 0, message = "Price cannot be negative")
    private float price;
    
  • Avoid manual transactions: Stick to container-managed transactions (CMT) unless you have a very specific need for bean-managed transactions (BMT)—CMT reduces code complexity and error risk.

内容的提问来源于stack exchange,提问作者SuperSpitter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:53:56