事务性 CDI bean 未将记录提交到数据库

Transactional CDI bean not committing record to database

我正在尝试使用 JAX-RS 构建一个简单的 REST 服务,它将在数据库 table 上执行标准的 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>

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

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

  1. 在您的 GlassFish 服务器实例
  2. 中配置连接池数据源 JNDI 名称
  3. Link 通过 <jta-data-source> 元素 persistence.xml 中的数据源配置

请查看此答案以获取更多详细信息:

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

查看 here 以了解 Spring JavaEE

中事务之间的详细信息

我在 weblogic 12.2.1 上试过你的应用程序,它成功地插入到数据库中,我对事务没有任何问题。

这是我的代码。

RecipeResource class(我修改了 @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 class 与您的相同,只是我对架构进行了评论:

@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>

所以您的问题可能来自应用程序服务器。

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