1

I am trying to build a simple REST service, using JAX-RS, that will perform the standard CRUD operations on a database table. I am able to successfully query for records, but I cannot insert new ones. I do not get any errors and when I step through the code in debug mode everything looks good. I am using a transactional CDI bean running in a Glassfish 4.1 container.

It feels like it's just never committing the transaction. I'm pretty new to Java EE, but my understanding is that since the bean is transactional the container should handle the commit for me. Anyone know why it is not?

@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>
Nikos Paraskevopoulos
  • 39,514
  • 12
  • 85
  • 90
lxdr
  • 451
  • 1
  • 5
  • 21
  • 1
    This does not look "transactional" to me. What do you think is making any of these beans "transactional"? – Steve C Jan 07 '17 at 23:48
  • @SteveC: The `RecipesService.addRecipe()` is `@Transactional`, so it should work... @lxdr: Are you sure you have the right `@Transactional`? Can you show your `persistence.xml`? Are there any qualifiers on the `RecipesService` not shown here? – Nikos Paraskevopoulos Jan 08 '17 at 17:44
  • @Nikos Paraskevopoulos: The import for `@Transactional` resolves to javax.transaction.Transactional. The only thing I left out of the class was boiler plate imports. I have added my persistence.xml too. Thanks! – lxdr Jan 08 '17 at 17:52
  • This is very strange! So, guessing: have you set up a datasource in Glassfish? If so have you tried to use GF's datasource using the `` element, instead of the direct connection `` elements? – Nikos Paraskevopoulos Jan 08 '17 at 19:35
  • How are you adding Hibernate to GlassFish? What version of hibernate? Do you have `hibernate.transaction.jta.platform` set anywhere? – John Ament Jan 08 '17 at 20:23
  • Sorry - I did not spot the `@Transactional` annotation on `addRecipe` – Steve C Jan 09 '17 at 02:36
  • Normally I would just mark one or both of `RecipeResource` and `RecipesService` as `@Stateless` and not worry about explicit transaction markup. – Steve C Jan 09 '17 at 02:38
  • @NikosParaskevopoulos the version of GF I am using does not allow you to add new connection pools (known defect apparently) so what I did was modify the existing one that comes with GF OOB to point to my embedded Derby DB. Aside from that I have not done any GF configuration. I am using Hibernate 5.2.5. I don't think I've ever done anything to add it to GF. I have tried converting both my beans to EJB's and my transaction still does not commit. I am wondering if this is a GF config issue. – lxdr Jan 09 '17 at 03:45
  • A few more thoughts: (1) I wonder if opening the Derby DB in embedded mode both from GF and your application has any implications, e.g. your application opening the connections read-only. I don't think so - but mentioning it just in case. (2) Since the datasource of GF already points to your DB, try the `` element in persistence.xml, *instead* of the direct connection `` elements. – Nikos Paraskevopoulos Jan 09 '17 at 09:12
  • @lxdr any thoughts to my hibernate question from above? – John Ament Jan 10 '17 at 03:50
  • @John Ament: I am using Hibernate 5.2.5. I don't think I've done anything to add it to GF. Thing is, it's querying my database just fine, and I can even see in the logs where it begins the transaction when persisting the record, it just never commits. – lxdr Jan 11 '17 at 02:49

3 Answers3

1

I tried your application on a weblogic 12.2.1 and it successfully inserted in database and i do not have any problem with transaction.

Here is my code.

RecipeResource class (I modified the @Path to call it via web browser and also instanciated the Recipe manually):

@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";
    }

}

The Recipe class is same as yours except that i commented the schema :

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

My persistence.xml (I'm using in-memory database):

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

So your problem might come from the Application Server.

Did you try to deploy your webapp on another server?

Rouliboy
  • 1,377
  • 1
  • 8
  • 21
  • OK so I copied your persistence.xml exactly, no changes at all, into my deployment, and cleaned and restarted GF. And it worked. Honestly I am a bit flabbergasted as I don't see how this could possibly work (I didn't even update `` to point to the package with my Recipe class). Since this is the closest thing to a working solution I have, I am accepting it for now. I intend to keep digging to see if I can figure out how GF and JPA work together. I've about had it with GF, its got so many bugs and its behavior is often bizarre, I might try a different server soon. Thanks for your help! – lxdr Jan 11 '17 at 03:23
0

When you are using JTA transaction management, responsibility for creating and managing database connections is provided by application server, not your application.

Basically, you have to configure your data source in your GlassFish server instance, not directly in persistence.xml via properties:

  1. Configure connection pool and datasource JNDI name in your GlassFish server instance
  2. Link data source configuration in your persistence.xml via <jta-data-source> element

Please check this answer for further details: https://stackoverflow.com/a/9137741/1980178

Community
  • 1
  • 1
sider
  • 193
  • 6
  • I must have missed that one when searching SO, thanks for pointing it out, my problem is very similar. @Nikos Paraskevopoulos: I added `jta-data-source>jdbc/__default` (jdbc/__default was already setup in GF) to my persistence.xml but I'm still getting the same behavior. If it helps at all I can see in this in the console: Info: Managed bean with "Transactional annotation and TxType of REQUIRED called outside a transaction context. Beginning a transaction..." So looks like the transaction is starting... – lxdr Jan 10 '17 at 02:26
-1

Are you sure you are not mixing two frameworks. RecipeResource has a @Path annotation which is from the JavaEE framework, and the @Transactional annotation is from the Spring framework, I think you should replace it with @TransactionAttribute which is the equivalent JavaEE anotation.

Have a look here for details between transaction in Spring an JavaEE

Klaus Groenbaek
  • 4,820
  • 2
  • 15
  • 30
  • 1
    @KlausGroenbaeck: Java EE 7 does have an `@Transactional` annotation (http://docs.oracle.com/javaee/7/api/javax/transaction/Transactional.html). Perhaps it was borrowed from Spring? I do know for sure that I don't have any Spring jars in my workspace, or even on my computer, so I'm not sure how I would be mixing frameworks. – lxdr Jan 10 '17 at 02:18
  • Sorry, you are right JavaEE7 also has a transactional annotation. – Klaus Groenbaek Jan 10 '17 at 16:45
  • Personally I have more or less stopped using the JavaEE framework. I prefer using Spring, it works the same way regardless of where you deploy it. – Klaus Groenbaek Jan 10 '17 at 16:47