[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
Re: [eclipselink-users] Spikes, FOSS JPA 1.0 test framework
|
Dear Shaun,
I was using EclipseLink M6, I just updated locally to M7 but still have the
problem. FYI Toplink Essentials is V2 B41.
I'll give you some code here, but you can get the whole thing from:
svn checkout https://lab.jugtorino.it/svn/sandbox/spikes/trunk spikes
The framework is using Spring 2.5.3 for the unit tests.
The base domain class (which contains the @PreUpdate) file is
org/syger/example/domain/AbstractModel.java:
<pre>
package org.syger.example.domain;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.PreUpdate;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;
/**
* Modello astratto: contiene proprieta' commune a tutte le entita' -
* Abstract model: contains properties common to all entities.
*
* @author john.leach
*/
@MappedSuperclass
public abstract class AbstractModel {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Long id;
@Version
@Column(nullable = false)
Long version;
@Column(updatable = false)
@Temporal(TemporalType.TIMESTAMP)
java.util.Date createdAt;
@Temporal(TemporalType.TIMESTAMP)
java.util.Date lastUpdated;
public Long getId() { return id; }
public Long getVersion() { return version; }
public java.util.Date getCreatedAt() { return createdAt; }
public java.util.Date getLastUpdated() { return lastUpdated; }
public AbstractModel() {
lastUpdated = createdAt = new java.util.Date();
}
/**
* Settaggio della data del'ultimo aggiornamento prima del salvataggio -
* Sets the last updated date prior to saving.
*/
@PreUpdate
protected void preUpdate() {
lastUpdated = new java.util.Date();
}
}
</pre>
The concrete User domain model (which has the not-null fields, and
hashCode() method that causes the NPE) file is
org/syger/example/domain/User.java:
<pre>
package org.syger.example.domain;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
/**
* User: un utente ha un nome (univoco) ed zero o piu' Client(i) -
* User: a user has a unique name and zero or more Client(s).
*
* @author john.leach
*/
@Entity
@Table(name="app_user", // user can be a reserved SQL word
uniqueConstraints=@UniqueConstraint(columnNames={"name"}))
public class User extends AbstractModel {
@Basic(optional = false)
@Column(nullable = false, updatable = false, length = 100)
String name;
@Basic(optional = false)
String info;
@OneToMany(mappedBy = "user", cascade = { CascadeType.ALL })
@OrderBy("name ASC")
List<Client> clients = new ArrayList<Client>();
public User() {
super();
info = "";
}
public User(String name) {
this();
this.name = name;
}
public String getName() { return name; }
public void setName(String name) { // needed for Yaml
if (this.name == null) {
this.name = name;
}
}
public List<Client> getClients() { return clients; }
public String getInfo() { return info; }
public void setInfo(String info) { this.info = info; }
public void addClient(Client newClient) {
newClient.setUser(this);
if (clients.contains(newClient)) {
return;
}
clients.add(newClient);
}
public void removeClient(Client oldClient) {
if (clients.remove(oldClient)) {
// oldClient.setUser(null); // removed to keep EclipseLink happy
}
}
public boolean equals(Object other) {
if (this == other) return true;
if (other == null) return false;
if ( !(other instanceof User) ) return false;
final User that = (User)other;
return this.name.equals(that.getName());
}
public int hashCode() {
return name.hashCode();
}
public String toString() {
return name;
}
}
</pre>
The test classes use an abstract class, which itself sits on the Spring
framework, file is org/syger/unittest/AbstractJpaExperimentalTests.java:
<pre>
package org.syger.unittest;
import org.springframework.test.jpa.AbstractJpaTests;
import org.syger.example.manager.DataManager;
/**
* Classe d'aiuto per tutte le unit tests -
* Helper class for all unit tests.
*
* @author john.leach
*/
public class AbstractJpaExperimentalTests extends AbstractJpaTests {
private DataManager dataManager;
protected String[] getConfigLocations() {
return new String[] { "classpath:META-INF/test-spring-config.xml" };
}
public DataManager getDataManager() { return this.dataManager; }
public void setDataManager(DataManager dataManager) { this.dataManager =
dataManager; }
public void log(String message) {
System.out.println(message);
System.err.println(message);
}
}
</pre>
Next is the null test which causes a wrapped org.springframework exception
in EclipseLink, but a java.lang.NPE in TopLink Essentials, plus the
@PreUpdate test, file org/syger/unittest/UserTest.java:
<pre>
package org.syger.unittest;
import org.syger.example.domain.User;
/**
* Test basiliare per il domain model User -
* Basic tests for the User domain model.
*
* @author john.leach
*/
public class UserTest extends AbstractJpaExperimentalTests {
public void testUserNullName() {
User user = new User();
try {
getDataManager().persist(user);
getDataManager().flush();
fail("Not null exception expected");
}
catch (Exception ex) {
if (!ex.getClass().getName().startsWith("org.springframework")) {
ex.printStackTrace(System.out);
fail("Not the expected exception " + ex.getClass().getName());
}
}
}
// ... other tests removed
public void testUserPreUpdate() {
User user = new User("updated");
getDataManager().persist(user);
getDataManager().flush();
java.util.Date preUpdated = user.getLastUpdated();
System.out.println("Original " + preUpdated.getTime());
long time = System.currentTimeMillis();
while (System.currentTimeMillis() - time < 1000L) {
// dum de dum... just waiting a second
}
user.setInfo("info");
getDataManager().flush();
java.util.Date postUpdated = user.getLastUpdated();
System.out.println("Changed " + postUpdated.getTime());
assertFalse("Date didn't change", preUpdated.getTime() ==
postUpdated.getTime());
}
}
</pre>
You probably want to see the persistence.xml file too (there is no orm.xml
file, or rather it is empty):
<pre>
<?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_1_0.xsd"
version="1.0">
<persistence-unit name="JPA-Experiments"
transaction-type="RESOURCE_LOCAL">
<description>The persistent unit for Example domain
classes.</description>
<class>org.syger.example.domain.AbstractModel</class>
<class>org.syger.example.domain.User</class>
<class>org.syger.example.domain.Client</class>
<class>org.syger.example.domain.Tagging</class>
<class>org.syger.example.domain.Tag</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<!-- Hibernate properties -->
<property name="hibernate.cfg.customizer"
value="org.syger.example.domain.HibernateConfiguration"/>
<!-- TopLink Essentials properties -->
<property name="persistence.tools.weaving" value="true"/>
<property name="toplink.session.customizer"
value="org.syger.example.domain.TopLinkConfiguration"/>
<!-- EclipseLink properties -->
<property name="persistence.tools.weaving" value="true"/>
<property name="eclipselink.session.customizer"
value="org.syger.example.domain.EclipseLinkConfiguration"/>
<!-- OpenJPA properties -->
<property name="openjpa.Log" value="DefaultLevel=TRACE,
Enhance=TRACE, MetaData=TRACE, Runtime=TRACE, Tool=TRACE, SQL=TRACE"/>
</properties>
</persistence-unit>
</persistence>
</pre>
Finally, just to make this post a little longer, first the TopLink
Essentials Spring config file:
<pre>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:annotation-config/>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation"
value="classpath:META-INF/persistence.xml"/>
<property name="persistenceUnitName" value="JPA-Experiments"/>
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter">
<property name="databasePlatform"
value="oracle.toplink.essentials.platform.database.HSQLPlatform"/>
<property name="showSql" value="true"/>
<property name="generateDdl" value="true"/>
</bean>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
destroy-method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:mem:testDb"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<bean
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="tagManager" class="org.syger.example.manager.TagManager"
scope="prototype"/>
<bean id="dataManager" class="org.syger.example.manager.DataManager"
scope="prototype">
<property name="tagManager" ref="tagManager"/>
</bean>
</beans>
</pre>
Then the EclipseLink Spring config file:
<pre>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:annotation-config/>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation"
value="classpath:META-INF/persistence.xml"/>
<property name="persistenceUnitName" value="JPA-Experiments"/>
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="databasePlatform"
value="org.eclipse.persistence.platform.database.HSQLPlatform"/>
<property name="showSql" value="true"/>
<property name="generateDdl" value="true"/>
</bean>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
destroy-method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:mem:testDb"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<bean
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="tagManager" class="org.syger.example.manager.TagManager"
scope="prototype"/>
<bean id="dataManager" class="org.syger.example.manager.DataManager"
scope="prototype">
<property name="tagManager" ref="tagManager"/>
</bean>
</beans>
</pre>
Again, you might find it quicker just to grab the Subversion trunk. I run
all the tests via an ant script within a DOS prompt, on Windows XP
Professional SP2. The computer was surrounded by a 3 inch thick anti-murphy
barrier, which I will be sending back to the manufacturers...
Sorry about the <pre></pre> marks, I thought they'd work
Hope that helps,
John
Shaun Smith wrote:
>
> Hi John,
>
>> Shaun Smith wrote:
>>
>>> In terms of the problem you're addressing, things have changed since you
>>> wrote the article.
>>>
>>
>> It's only a couple of week old!
>>
> No one can say we're sitting around doing nothing! ;-)
> ...
>> FYI, I have now updated Spikes to use TopLink Essentials, and there have
>> been a couple of problems:
>>
>> 1. Deliberately setting a non null field to null, and then attempting to
>> persist the object does not give back a Spring wrapped exception (as it
>> does
>> in EclipseLink), but rather the infamous NPE trying to call hashCode -
>> which
>> I assume means that TopLink is trying to put the object in its own cache,
>> without any 'formal' validation checks first - that's my hypothesis, more
>> than likely to be wrong. Here's a piece of the stack dump:
>>
> Can you post the class you're trying to persist and it's mappings?
> ...
>> 2. Both TopLink and EclipseLink fail to update a @PreUpdate annotated
>> method
>> when that method is not public - when it's public things work just fine.
>> Yet the specs state that (section 3.5.1)
>>
>> The callback methods can have public, private, protected, or package
>> level
>> access, but must not be
>> static or final.
>>
>> Here's the method:
>>
>> /**
>> * Settaggio della data del'ultimo aggiornamento prima del salvataggio -
>> * Sets the last updated date prior to saving.
>> */
>> @PreUpdate
>> protected void preUpdate() {
>> lastUpdated = new java.util.Date();
>> }
>>
>> Change protected to public and Robert is you proverbial father's brother.
>> This is not a showstopper of course, I'm personally not that squeemish
>> about
>> making the method public.
>>
> I couldn't reproduce this problem with EclipseLink M7. I began a
> transaction, queried an object, modified a String field, and committed
> the transaction. I tried package, private, protected, and public. Each
> time my @PreUpdate method was called.
>
> Shaun
> --
>
>
> Oracle <http://www.oracle.com>
> Shaun Smith | Principal Product Manager, TopLink | +1.905.502.3094
> Oracle Fusion Middleware
> 110 Matheson Boulevard West, Suite 100
> Mississauga, Ontario, Canada L5R 3P4
>
> _______________________________________________
> eclipselink-users mailing list
> eclipselink-users@xxxxxxxxxxx
> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
>
>
--
View this message in context: http://www.nabble.com/Spikes%2C-FOSS-JPA-1.0-test-framework-tp17201935p17349805.html
Sent from the EclipseLink - Users mailing list archive at Nabble.com.