19

I'm trying to setup Spring using Hibernate and JPA, but when trying to persist an object, nothing seems to be added to the database.

Am using the following:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="url" value="${jdbc.url}"/>
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="persistenceUnitName" value="BankingWeb" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="generateDdl" value="true" />
            <property name="showSql" value="true" />
            <property name="databasePlatform" value="${hibernate.dialect}" />
        </bean>
    </property>
</bean>

<tx:annotation-driven/>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

<bean name="accountManager" class="ssel.banking.dao.jpa.AccountManager" />
<bean name="userManager" class="ssel.banking.dao.jpa.UserManager" />

And in AccountManager, I'm doing:

@Repository
public class AccountManager implements IAccountManager {

    @PersistenceContext private EntityManager em;

    /* -- 8< -- Query methods omitted -- 8< -- */

    public Account storeAccount(Account ac) {
    ac = em.merge(ac);
        em.persist(ac);
        return ac;
    }
}

Where ac comes from:

    Account ac = new Account();
    ac.setId(mostRecent.getId()+1);
    ac.setUser(user);
    ac.setName(accName);
    ac.setDate(new Date());
    ac.setValue(0);
    ac = accountManager.storeAccount(ac);
    return ac;

Is there anyone who can point out what I'm doing wrong? The persist call returns without throwing exceptions. If afterwards I do em.contains(ac), this returns true.

In case anyone needed, here's how Account is defined:

@SuppressWarnings("serial")
@Entity
@NamedQueries({
        @NamedQuery(name = "Account.AllAccounts", query = "SELECT a FROM Account a"),
        @NamedQuery(name = "Account.Accounts4User", query = "SELECT a FROM Account a WHERE user=:user"), 
        @NamedQuery(name = "Account.Account4Name", query = "SELECT a FROM Account a WHERE name=:name"),
        @NamedQuery(name = "Account.MaxId", query = "SELECT MAX(a.id) FROM Account a"),
        @NamedQuery(name = "Account.Account4Id", query = "SELECT a FROM Account a WHERE id=:id"),
    })
public class Account extends AbstractNamedDomain {
    @Temporal(TemporalType.DATE)
    @Column(name = "xdate")
    private Date date;

    private double value;

    @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE})
    @JoinColumn(name="userid")
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE}, fetch=FetchType.EAGER)
    @OrderBy("date")
    private List<AccountActivity> accountActivity = new ArrayList<AccountActivity>();

    public List<AccountActivity> getAccountActivity() {
        return accountActivity;
    }

    public void setAccountActivity(List<AccountActivity> accountActivity) {
        this.accountActivity = accountActivity;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public double getValue() {
        return value;
    }

    public void setValue(double value) {
        this.value = value;
    }

    public void addAccountActivity(AccountActivity activity) {
        // Make sure ordering is maintained, JPA only does this on loading
        int i = 0;
        while (i < getAccountActivity().size()) {
            if (getAccountActivity().get(i).getDate().compareTo(activity.getDate()) <= 0)
                break;
            i++;
        }
        getAccountActivity().add(i, activity);
    }
}

@MappedSuperclass public abstract class AbstractNamedDomain extends AbstractDomain {

    private String name;

    public AbstractNamedDomain() {

    }

    public AbstractNamedDomain(String name) {

        this.name = name;
    }

    public String getName() {
        return name;
    }

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

@MappedSuperclass public abstract class AbstractDomain implements Serializable {

    @Id @GeneratedValue
    private long id = NEW_ID;

    public static long NEW_ID = -1;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id; 
    }

    public boolean isNew() {

        return id==NEW_ID;
    }
}
Ruben Vermeersch
  • 1,933
  • 1
  • 18
  • 27
  • hey ruben, i'm doing something similar, but i have a persistence.xml file, do you have one, why or why not? thanks – bmw0128 Jul 15 '09 at 16:36

4 Answers4

18

Thanks to eric and Juan Manuel's answers, I was able to figure out that the transaction wasn't committed.

Adding @Transactional to the storeAccount method did the trick!

Ruben Vermeersch
  • 1,933
  • 1
  • 18
  • 27
  • Is that all you did to fix the issue? – James McMahon Apr 24 '09 at 15:51
  • In my case I had to add @Transactional to the entry point of my class. Before I just had @Transactional on my method that did the persisting. Not sure if I completely understand what is going on there. – James McMahon Apr 24 '09 at 18:32
  • Works!...it seems when the @Transactional is added to a specific method, it automatically specifies the transaction stages (begin,commit, flush etc) – Kermit_ice_tea Oct 20 '14 at 01:58
5

I actually came across another way that this can happen.

In an injected EntityManager, no updates will ever occur if you have persistence.xml specified like this:

<persistence-unit name="primary" transaction-type="RESOURCE_LOCAL">

You need to remove the transaction-type attribute and just use the default which is JTA.

Ruben Vermeersch
  • 1,933
  • 1
  • 18
  • 27
Nicholas DiPiazza
  • 10,029
  • 11
  • 83
  • 152
1

Probably you're keeping the transaction active and it is not calling "commit" until other methods running supporting the active transaction end (all "voting" for commit and none for rollback.)

If you're sure that the entity you're going to persist is ok you could probably do this:

@TransactionManagement(TransactionManagementType.BEAN)
public class AccountManager implements IAccountManager { ..}

and manage your entity persistance adding:

@Resource
private SessionContext sessionContext;

public Account storeAccount(Account ac) {
    sessionContext.getUserTransaction().begin();
    ac = em.merge(ac);
    em.persist(ac);
    sessionContext.getUserTransaction().commit();
    return ac;
}
Adeel Ansari
  • 39,541
  • 12
  • 93
  • 133
Juan Manuel
  • 178
  • 12
1

You do not explicitly need to call the persist method. The merge operation would write the changes to the DB upon commit.