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

Stripes更新对象时创建新对象:如何复用分离脏态对象?

Reusing Detached JDO Objects with Stripes on Google App Engine

Great question—this is a common gotcha when combining Stripes' default parameter binding with DataNucleus JDO's detached state management on GAE. The short answer: yes, you can absolutely reuse the detached clean object you fetched, modify it, and persist it in a detached dirty state. Here’s how to make it work:

1. Override Stripes' Default Object Population

Stripes defaults to creating a new transient instance of your entity class to bind request parameters to. To fix this, you need to tell Stripes to use the existing detached object you loaded from the database instead.

Option A: Manual Parameter Binding (Simple & Straightforward)

In your ActionBean, first fetch the detached object in your edit handler, then manually map request parameters to this instance in your save handler:

public class ProductAction implements ActionBean {
    private ActionBeanContext context;
    private Product product; // Our detached object
    private Long productId;

    @DefaultHandler
    public Resolution edit() {
        // Fetch the entity from GAE datastore and detach it
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
            Product persistedProduct = pm.getObjectById(Product.class, productId);
            product = pm.detachCopy(persistedProduct); // Clean detached state
        } finally {
            pm.close();
        }
        return new ForwardResolution("/product-edit.jsp");
    }

    public Resolution save() {
        // Manually bind request params to our existing detached product
        HttpServletRequest request = context.getRequest();
        product.setName(request.getParameter("name"));
        product.setPrice(Double.parseDouble(request.getParameter("price")));
        product.setDescription(request.getParameter("description"));

        // Persist the dirty detached object
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
            pm.makePersistent(product); // DataNucleus detects changes and runs UPDATE
        } finally {
            pm.close();
        }
        return new RedirectResolution(ProductAction.class, "list");
    }

    // Getters and setters for context, productId, product
}

Option B: Custom Population Strategy (Scalable)

If you don’t want to manually bind every field, create a custom PopulationStrategy to reuse existing instances in your ActionBean:

public class ReuseDetachedEntityStrategy extends DefaultPopulationStrategy {
    @Override
    public Object getInstance(ActionBean bean, Class<?> type, String name) {
        // Check if the ActionBean has an existing instance of the entity
        if (type == Product.class && bean instanceof ProductAction) {
            ProductAction action = (ProductAction) bean;
            if (action.getProduct() != null) {
                return action.getProduct(); // Return the pre-loaded detached object
            }
        }
        // Fall back to default behavior for other cases
        return super.getInstance(bean, type, name);
    }
}

Then annotate your ActionBean to use this strategy:

@PopulationStrategy(ReuseDetachedEntityStrategy.class)
public class ProductAction implements ActionBean {
    // ... rest of your code, including loading the detached product in edit()
}

Now Stripes will bind request parameters directly to your existing detached object instead of creating a new transient one.

2. Ensure DataNucleus Tracks Detached Dirty State

When you fetch and detach the object using pm.detachCopy(), DataNucleus automatically retains the original field values to track changes. As long as you modify the same detached instance, calling pm.makePersistent() will trigger an UPDATE (not INSERT) because DataNucleus recognizes it as a detached dirty object, not a transient one.

For complex entities with relationships, you might need to adjust the fetch plan when detaching to include related objects:

pm.getFetchPlan().setGroup("detach-group"); // Define a fetch group in your JDO metadata
product = pm.detachCopy(persistedProduct);

3. GAE-Specific Checks

Google App Engine’s DataNucleus implementation fully supports detached dirty state management, so you don’t need any extra configuration here—just ensure you’re using the official GAE JDO dependencies and that your persistence.xml is correctly set up for GAE.


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:20:34