简体   繁体   中英

Cannot bring EclipseLink to use methods instead of fields

I am fairly new to JPA and I am trying around in combination with EclipseLink 2.6.1-RC1 and sqlite 3.8.10.1 .

My actual challenge is to let the Persistence Provider use the get-/set-methods instead of direct field access.

I am having much trouble with EclipseLink and currently I am comparing my simple test project with Hibernate and there's as far as I can see no problem with Hibernate.

Do I use JPA in an incorrect way or are my errors EclipseLink bugs?

Here's my example:

persistence.xml :

<persistence-unit name="inmemorydb" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
        <property name="eclipselink.target-database" value="Database" />
        <property name="javax.persistence.jdbc.url" value="jdbc:sqlite::memory:"/>
        <property name="javax.persistence.jdbc.driver" value="org.sqlite.JDBC"/>
    </properties>
</persistence-unit>

SimpleEntity.java :

@Entity
public class SimpleEntity implements Serializable {
private static final long serialVersionUID = 1L;

@Id
private long id;

@Transient
private String otherName;

public long getId() {
    return id;
}

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

@Basic
@Column(name="name")
public String getOtherName() {
    System.out.println("getOtherName: " + otherName);
    return otherName;
}

public void setOtherName(String otherName) {
    System.out.println("setOtherName: " + otherName);
    this.otherName = otherName;
}
}

JpaTest.java :

public class JpaTest {

public static final String PERSISTENCE_UNIT = "inmemorydb";

public static final String SQL_CREATE_TABLE = "CREATE TABLE simpleentity (id INTEGER PRIMARY KEY, name TEXT);";

private static EntityManagerFactory dbConnectionPool;

@BeforeClass
public static void initialiseDatabase() {
    System.out.println("initialiseDatabase");
    dbConnectionPool = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);
    EntityManager dbConnection = dbConnectionPool.createEntityManager();
    dbConnection.getTransaction().begin();

    Query createTableQuery = dbConnection.createNativeQuery(SQL_CREATE_TABLE);
    createTableQuery.executeUpdate();

    dbConnection.getTransaction().commit();
    dbConnection.close();
}

@Test
public void createTable_InsertEntity() {
    System.out.println("createTable_InsertEntity");
    EntityManager dbConnection = dbConnectionPool.createEntityManager();
    dbConnection.getTransaction().begin();

    SimpleEntity testEntity = new SimpleEntity();
    testEntity.setId(1);
    testEntity.setOtherName("my name");
    dbConnection.persist(testEntity);

    dbConnection.getTransaction().commit();
    dbConnection.close();
}

@Test
public void createTable_InsertEntity_ReadEntity() {
    System.out.println("createTable_InsertEntity_ReadEntity");
    EntityManager dbConnection = dbConnectionPool.createEntityManager();
    dbConnection.getTransaction().begin();

    SimpleEntity testEntity = new SimpleEntity();
    testEntity.setId(2);
    testEntity.setOtherName("my other name");
    dbConnection.persist(testEntity);

    dbConnection.getTransaction().commit();
    dbConnection.getTransaction().begin();

    TypedQuery<SimpleEntity> findEntityQuery = dbConnection.createQuery("SELECT e FROM SimpleEntity e WHERE e.id = 2", SimpleEntity.class);
    SimpleEntity foundEntity = findEntityQuery.getSingleResult();
    dbConnection.close();

    assertEquals(2, foundEntity.getId());
    assertEquals("my other name", foundEntity.getOtherName());
}
}

As you may assume I would expect to see EclipseLink calling my getOtherName() or setOtherName(String) (I'm not sure about that). But the simplified result is:

initialiseDatabase
[EL Info]: server: 2015-09-16 01:07:07.877--Detected server platform: org.eclipse.persistence.platform.server.NoServerPlatform.
[EL Info]: server: 2015-09-16 01:07:07.969--ServerSession(1845066581)--Detected server platform: org.eclipse.persistence.platform.server.NoServerPlatform.
 [EL Info]: server: 2015-09-16 01:07:08.184--Detected server platform: org.eclipse.persistence.platform.server.NoServerPlatform.
[EL Info]: server: 2015-09-16 01:07:08.184--ServerSession(1845066581)--Detected server platform: org.eclipse.persistence.platform.server.NoServerPlatform.
[EL Info]: 2015-09-16 01:07:08.187--ServerSession(1845066581)--EclipseLink, version: Eclipse Persistence Services - 2.6.1.v20150605-31e8258
[EL Info]: connection: 2015-09-16 01:07:09.017--ServerSession(1845066581)--/file:/C:/eclipse_ee/workspaces/jpaproperty/target/classes/_inmemorydb login successful
createTable_InsertEntity_ReadEntity
setOtherName: my other name
getOtherName: my other name
createTable_InsertEntity
setOtherName: my name

The printed calls of the set and get method are my calls within the test class - so no single call of EclipseLink ? I really tried a lot with different settings of @Transient , @Basic , @Column but without success.

When I try to use the so called standard of @Access(AccessType.PROPERTY) I even get a very strange error:

Exception [EclipseLink-30005] (Eclipse Persistence Services - 2.6.1.v20150605-31e8258): org.eclipse.persistence.exceptions.PersistenceUnitLoadingException
Exception Description: An exception was thrown while searching for persistence archives with ClassLoader: sun.misc.Launcher$AppClassLoader@29453f44
Internal Exception: javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.6.1.v20150605-31e8258): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [inmemorydb] failed.
Internal Exception: java.lang.ClassCastException: org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataField    cannot be cast to org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataMethod

Isn't this JPA standard and isn't EclipseLink conforming to this?

By the way, when I use Hibernate the output is the following:

createTable_InsertEntity
setOtherName: my name
getOtherName: my name
getOtherName: my name
getOtherName: my name
createTable_InsertEntity_ReadEntity
setOtherName: my other name
getOtherName: my other name
getOtherName: my other name
getOtherName: my other name
getOtherName: my other name
getOtherName: my other name
getOtherName: my other name

Can you help me - please try not to convince me about Hibernate - my first target is to understand what is JPA standard and what is not!

It's not clear from your question how you used the @Access annotations; but you need to specify in two places: once on the class to specify the default for the entity's attributes, and on any attribute that does not use the default access. So: your example should look like this:

@Entity
@Access(AccessType.FIELD)
public class SimpleEntity implements Serializable {
private static final long serialVersionUID = 1L;

@Id
private long id;

@Transient
private String otherName;

public long getId() {
    return id;
}

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

@Basic
@Column(name="name")
@Access(AccessType.PROPERTY)
public String getOtherName() {
    System.out.println("getOtherName: " + otherName);
    return otherName;
}

public void setOtherName(String otherName) {
    System.out.println("setOtherName: " + otherName);
    this.otherName = otherName;
}
}

Don't use @Transient over otherName .

Setting JPA annotations over methods (getters) has the same sense like over fields, its only different "dialect". Eclipselink (in my opinion) requires to be consequent in one way, Hibernate is more tolerant (can mix field / method annotations)

In my personal work never use @Access(AccessType.PROPERTY) over members - if above rules are satisfied auto configuration was OK.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM