【问题标题】:Transactional CDI bean not committing record to database事务 CDI bean 未将记录提交到数据库
【发布时间】:2017-01-07 20:57:06
【问题描述】:

我正在尝试使用 JAX-RS 构建一个简单的 REST 服务,它将在数据库表上执行标准的 CRUD 操作。我能够成功查询记录,但无法插入新记录。我没有收到任何错误,当我在调试模式下单步执行代码时,一切看起来都很好。我正在使用在 Glassfish 4.1 容器中运行的事务 CDI bean。

感觉就像它永远不会提交事务。我对 Java EE 很陌生,但我的理解是,由于 bean 是事务性的,因此容器应该为我处理提交。有谁知道为什么没有?

@Path("/recipes")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class RecipeResource {
    @Inject
    RecipesService recipesService;

    @GET
    public List<Recipe> getRecipes() {
        return recipesService.getAllRecipes();
    }

    @POST
    public void addRecipse(Recipe recipe) {
        recipesService.addRecipe(recipe);
    }

}

public class RecipesService {
    @PersistenceContext(unitName="PANTRYDB", type=PersistenceContextType.TRANSACTION)
    EntityManager em;

    public RecipesService () {

    }

    public List<Recipe> getAllRecipes () {
        List<Recipe> recipes = null;

        try {
            TypedQuery<Recipe> typedQuery = em.createQuery("select r from Recipe r", Recipe.class);

            recipes = typedQuery.getResultList();
        } catch (Exception e) {
            System.out.println(e);
        }

        return recipes;
    }

    @Transactional
    //This is the method that seems to not commit it's transaction
    //The Recipe object is populated correctly, and the persist() doesn't 
    //throw any errors
    public void addRecipe(Recipe recipe) {
        try {
            em.persist(recipe);
        } catch (Exception e) {
            System.out.println(e);
        }
    }

}

@Entity
@Table(name="RECIPES", schema="COOKBOOK")
public class Recipe {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;

    @Column
    private String name;

    @Column(name="CREATED_DATE")
    private Calendar createdDate;

    @Column(name="LAST_MADE_DATE")
    private Calendar lastMadeDate;

    @Column
    private String description;

    @Column
    private String notes;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Calendar getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Calendar createdDate) {
        this.createdDate = createdDate;
    }

    public Calendar getLastMadeDate() {
        return lastMadeDate;
    }

    public void setLastMadeDate(Calendar lastMadeDate) {
        this.lastMadeDate = lastMadeDate;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getNotes() {
        return notes;
    }

    public void setNotes(String notes) {
        this.notes = notes;
    }

    @Override
    public String toString() {
        return name;
    }
}

Persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="PANTRYDB" transaction-type="JTA">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <class>com.domain.Recipe</class>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />      
            <property name="javax.persistence.jdbc.url" value="jdbc:derby:/Users/development/eclipse/ws_playground/databases/pantry_db/PANTRYDB" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.DerbyDialect"/>
            <property name="javax.persistence.jdbc.user" value=""/>
            <property name="javax.persistence.jdbc.password" value=""/>
        </properties>
    </persistence-unit>
</persistence>

【问题讨论】:

  • 这对我来说看起来不像“交易”。你认为是什么让这些 bean 成为“事务性”的?
  • @SteveC:RecipesService.addRecipe()@Transactional,所以它应该可以工作... @lxdr:你确定你有正确的@Transactional 吗?你能显示你的persistence.xml吗? RecipesService 上是否有任何限定词未在此处显示?
  • @Nikos Paraskevopoulos:@Transactional 的导入解析为 javax.transaction.Transactional。我在课堂上唯一遗漏的是样板进口。我也添加了我的 persistence.xml。谢谢!
  • 这很奇怪!那么,猜测一下:您是否在 Glassfish 中设置了数据源?如果是这样,您是否尝试过使用&lt;jta-data-source&gt; 元素而不是直接连接&lt;property&gt; 元素来使用GF 的数据源?
  • 如何将 Hibernate 添加到 GlassFish?什么版本的休眠?你在任何地方设置了hibernate.transaction.jta.platform 吗?

标签: jpa jakarta-ee cdi


【解决方案1】:

我在 weblogic 12.2.1 上尝试了您的应用程序,它成功插入数据库,我的事务没有任何问题。

这是我的代码。

RecipeResource 类(我修改了@Path 以通过网络浏览器调用它并手动实例化Recipe):

@Path("/recipes")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)

public class RecipeResource {

    @Inject
    RecipesService recipesService;

    @GET
    @Path("get")
    public List<Recipe> getRecipes() {
        return recipesService.getAllRecipes();
    }

    @GET
    @Path("add")
    public String addRecipse() {
        Recipe recipe = new Recipe();
        recipe.setDescription("desc");
        recipesService.addRecipe(recipe);
        return "OK";
    }

}

Recipe 类与您的类相同,只是我评论了架构:

@Entity
@Table(name="RECIPES") //, schema="COOKBOOK")
public class Recipe {
}

我的persistence.xml(我正在使用内存数据库):

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns    /persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

   <persistence-unit name="PANTRYDB" transaction-type="JTA">
            <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/__default</jta-data-source>
    <class>org.jvi.webservice.transactional.db.Recipe</class>
    <properties>
        <!--<property name="eclipselink.ddl-generation" value="create-tables"/>-->
        <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
        <property name="eclipselink.logging.level" value="FINE"/>
        <property name="eclipselink.logging.level.sql" value="FINE"/>
        <property name="eclipselink.logging.parameters" value="true"/>
        <property name="eclipselink.logging.logger" value="DefaultLogger"/>
        <property name="eclipselink.cache.shared.default" value="false"/>
    </properties>
</persistence-unit>

所以你的问题可能来自应用服务器。

您是否尝试在另一台服务器上部署您的 web 应用程序?

【讨论】:

  • 好的,所以我将您的 persistence.xml 完全复制到了我的部署中,完全没有任何更改,然后清理并重新启动了 GF。它奏效了。老实说,我有点吃惊,因为我不知道这怎么可能起作用(我什至没有更新&lt;class&gt; 以指向带有我的Recipe 类的包)。由于这是我拥有的最接近工作解决方案的解决方案,因此我现在接受它。我打算继续挖掘,看看我是否能弄清楚 GF 和 JPA 是如何协同工作的。我和 GF 差不多,它有很多错误,而且它的行为经常很奇怪,我可能很快就会尝试不同的服务器。感谢您的帮助!
【解决方案2】:

当您使用 JTA 事务管理时,创建和管理数据库连接的责任由应用程序服务器提供,而不是您的应用程序。

基本上,您必须在 GlassFish 服务器实例中配置数据源,而不是通过属性直接在 persistence.xml 中配置:

  1. 在 GlassFish 服务器实例中配置连接池数据源 JNDI 名称
  2. 通过 &lt;jta-data-source&gt; 元素在您的 persistence.xml 中链接数据源配置

请查看此答案以获取更多详细信息: https://stackoverflow.com/a/9137741/1980178

【讨论】:

  • 我在搜索时一定错过了那个,谢谢指出,我的问题非常相似。 @Nikos Paraskevopoulos:我在我的 persistence.xml 中添加了 jta-data-source&gt;jdbc/__default&lt;/jta-data-source&gt;(jdbc/__default 已经在 GF 中设置),但我仍然得到相同的行为。如果它有帮助,我可以在控制台中看到:Info: Managed bean with "Transactional annotation and TxType of REQUIRED 在事务上下文之外调用。开始事务......" 所以看起来事务正在开始......
【解决方案3】:

您确定没有混合使用两个框架。 RecipeResource 有一个来自 JavaEE 框架的 @Path 批注,而 @Transactional 批注来自 Spring 框架,我认为您应该将其替换为 @TransactionAttribute 这是等效的 JavaEE 注释。

查看here 了解 Spring 和 JavaEE 中事务之间的详细信息

【讨论】:

  • @KlausGroenbaeck:Java EE 7 确实有一个 @Transactional 注释 (docs.oracle.com/javaee/7/api/javax/transaction/…)。也许它是从春天借来的?我确实知道我的工作区甚至计算机上都没有任何 Spring jar,所以我不确定如何混合框架。
  • 对不起,你是对的 JavaEE7 也有事务注解。
  • 就我个人而言,我或多或少地停止使用 JavaEE 框架。我更喜欢使用 Spring,无论您将它部署在哪里,它的工作方式都是一样的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-29
  • 1970-01-01
  • 2017-08-17
  • 1970-01-01
  • 2017-06-17
  • 2015-01-31
相关资源
最近更新 更多