Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JPA 1-1 is not supported where the user wants to store the "related object" reusing a column of something else #4

Open
andyjefferson opened this issue Apr 11, 2016 · 2 comments

Comments

@andyjefferson
Copy link
Member

== When a join is performed, if the field keys are of type string, the operation works,
however if the field keys are bigint, or int the join fails as the wrong row.getXXX() operation
is called ==

  1. Created 2 classes as below, the main feature of which is the fields used in the join are of type Long.

public class User implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Basic(optional = false)
@Column(name = "user_id", nullable = false)
private Long userId;

@Basic(optional = false)
@Column(name = "email", nullable = false, length = 255)
private String email;

@Basic(optional = false)
@Column(name = "password", nullable = false, length = 64)
private String password;

.... constructor, getters and setters....
}

public class Notepad implements Serializable {

@Id
@Basic(optional = false)
@Column(name = "notepad_id", nullable = false)
private Long notepadId;

@Basic(optional = false)
@Column(name = "user_id_notepad", nullable = false)
private Long userIdNotepad;

@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id_notepad", referencedColumnName = "user_id", insertable = false, updatable = false)
private User user;

.... contructor, getter and setter ....
}

  1. When JPA is used to fetch the Notepad entity e.g. via a query method such as :

    final TypedQuery q = em.createNamedQuery("Notepad.findByNotepadId", Notepad.class);
    q.setParameter("notepadId", 123L);
    List list = q.getResultList();

Though this could be any type of fetch eg. em.find(NotePad.class, 123L)

  1. The fetch fails, with for following output:

SEVERE: EjbTransactionUtil.handleSystemException: Value user_id is of type bigint
com.datastax.driver.core.exceptions.InvalidTypeException: Value userid_id is of type bigint
at com.datastax.driver.core.AbstractGettableByIndexData.checkType(AbstractGettableByIndexData.java:89)
at com.datastax.driver.core.AbstractGettableByIndexData.getString(AbstractGettableByIndexData.java:212)
at com.datastax.driver.core.AbstractGettableData.getString(AbstractGettableData.java:26)
at com.datastax.driver.core.AbstractGettableData.getString(AbstractGettableData.java:139)
at org.datanucleus.store.cassandra.fieldmanager.FetchFieldManager.fetchNonEmbeddedObjectField(FetchFieldManager.java:241)
at org.datanucleus.store.cassandra.fieldmanager.FetchFieldManager.fetchObjectField(FetchFieldManager.java:226)
at org.datanucleus.state.AbstractStateManager.replacingObjectField(AbstractStateManager.java:1590)

  1. What is happening is that the com.datastax.driver.core.AbstractGettableData.getString(AbstractGettableData.java:139)
    is getting called by default, assuming the user_id field is a string, however its a big int, so the getString() call fails.
  2. What should happen in: org.datanucleus.store.cassandra.fieldmanager.FetchFieldManager.java,

Method: protected Object fetchNonEmbeddedObjectField(AbstractMemberMetaData mmd, RelationType relationType, ClassLoaderResolver clr)
230 {
231 int fieldNumber = mmd.getAbsoluteFieldNumber();
232 MemberColumnMapping mapping = getColumnMapping(fieldNumber);
233
234 if (RelationType.isRelationSingleValued(relationType))
235 {
236 if (row.isNull(mapping.getColumn(0).getName()))
237 {
238 return null;
239 }
240

241 Object value = row.getString(mapping.getColumn(0).getName());
242 return getValueForSingleRelationField(mmd, value, clr);
243 }
244 else if (RelationType.isRelationMultiValued(relationType))
245 {
.....

In the code above, line 241, here it should test the column type, using mapping.getColumn(0).getTypeName()
and if it is a 'bigint' say, then do a call on row.getLong(mapping.getColumn(0).getName()) etc.

This also applies for data types 'bigint' - row.getLong(..),
'int' - row.getInt()
if it is neither of these, then the default would be to call row.getString() as before.

@andyjefferson
Copy link
Member Author

I think I understand more now about why my proposed fix won't work

  • when a one-to-one relationship field is present in an entity some additional information is stored into the
    database i.e. a 'persistence id' by and for Datanucleus when persist is done.

The problem here for me is that some other application (non Datanucleus is storing the data and has no
knowledge of a persistenceId), I am just reading data already stored, effectively doing a read only one-to-one
based on the Id for the entity.

So I think a work around for me would be to simplify my entity, remove the @OnetoOne mapping,
possibly mark the field as @transient and then manually fetch the field for myself within my application.

It would be good if the Datanucleus API could work without storing its own persistence id into the database
and just rely on the annotation mappings to fetch the object data.

@andyjefferson
Copy link
Member Author

NUCCASSANDRA-62.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant