My JPA/Hibernate odyssey continues...
I am trying to work around this issue, and so I have had to define primitive #Ids in my class that uses 3 entity fields as a composite key. This seems to get me a bit further, but now I'm getting this when persisting:
javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of com.example.model.LanguageSkill.stafferId
Here's my composite class:
public class LanguageSkill implements Serializable
{
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column(name = "Staffer_ID")
private Long stafferId;
#Id
#ManyToOne(cascade = CascadeType.ALL)
#MapsId(value = "stafferId")
private Staffer staffer;
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column(name = "Language_ID")
private Long languageId;
#ManyToOne
#MapsId(value= "languageId")
private Language language;
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column(name = "Language_Proficiency_ID")
private Long languageProficiencyId;
#ManyToOne
#MapsId(value= "languageProficiencyId")
private LanguageProficiency languageProficiency;
}
I do have proper getters and setters (IDE-generated) for both the primitives as well as the entities.
Here are my libs. I'm not totally convinced that I'm using a compatible set of persistence libraries (references to a cookbook detailing how to properly mix-and-match these would be highly appreciated.)
Hibernate 3.5.6-SNAPSHOT
hibernate-jpamodelgen 1.1.0.CR1
hibernate-validator 3.1.0.GA
MySQL 5.1.6
jsr250-api 1.0
javax.validation validation-api 1.0.0.GA
Wow, it's frustrating. 3 days now full time trying to solve various issues like this just for basic ORM. I feel defective. :-(
It seems a correct code. I had problem with this exception when I used Blob[]
#Lob
#Column(name="DOCUMENTO",nullable=false)
private Blob[] documento;
But changing by Byte[], I solved this problem.
I have only a occurrence, looking Oracle data types, I have seen this LONG is Character data of variable length (A bigger version the VARCHAR2 datatype).
I assume that your ID is a Integer....Why not change Long by Integer? You must remember that it only accepts primitive types.
This is my code and it works fine:
#Id
#SequenceGenerator(sequenceName="SQ_DOCUMENTO",name="seqDocumento")
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="seqDocumento")
private Integer idDocumento;
I use Hibernate 3.5.6-final, Spring 3.0.4, Junit 4 and Oracle 11g.
You have to remove the #GeneratedValue annotations.
Related
I am using spring-data-jpa and I have an entity with the following code:
#Id
#GeneratedValue
#Column(columnDefinition = "uuid")
private UUID id;
#Basic(optional = false)
#Column(updatable = false)
private long creationTimestamp;
...
I use a org.springframework.data.jpa.repository.JpaRepository to do the pertinent CRUD operations.
When I firstly save the entity (e.g. creationTimestamp=1), the method save(entity) returns the saved entity (like the javadoc says) with the new id for further operations (like the javadoc says) and with the field creationTimestamp=1.
But later on, if I try to update this entity with a new creationTimestamp (e.g. creationTimestamp=2), again with the method save(entity), this method returns the entity with the field creationTimestamp=2 (which is not correct).
If I search with the method findById(given_id), the returned entity has the field creationTimestamp=1, which is correct because the column was defined as updatable=false.
The question is, why when I update, the method save(entity) returns the new value in creationTimestamp instead of the one that is in the database? Because I expect "the saved entity" (like the javadoc says).
This option does not make the attribute write protected. It means that the UPDATE statement sent to the database shall exclude this field.
How #Stefan said #Column(updatable = false) affects only database layer
Here in Stackoverflow, there are a question about this
If I understood that you are trying to do, I recommend that Spring manage this column with
#Id
#GeneratedValue
#Column(columnDefinition = "uuid")
private UUID id;
#Basic(optional = false)
#CreationTimestamp
private java.sql.Timestamp creationTimestamp;
Using this implementation it isn't necessary you to code to set value in this attribute. You only create getter method.
Now, we define the id as String type with JPA annotation:
#Id
private String id;
Now we want to save UUID as binary in Mysql, I know JPA have one way to implement it like below:
#Id
#Column(columnDefinition = "BINARY(16)")
private UUID id;
But it is big effort to modify String to java.util.UUID type, because I need to modify huge code (a lots of test cases, other calls and so on, anyway we can't do this).
Then I try to use JPA Converter to convert String to bytes and save it, but I found JPA doesn't allow define converter on ID field.
So, please who could provide some possible ways for saving UUID as binary without changing the original String type.
You can use uuid2 ,it offers a broader type range to choose from:
java.lang.UUID
a 16 byte array
a hexadecimal String value
your id will like :
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(columnDefinition = "BINARY(16)")
#Id
private UUID id;
I made a NamedQuery that works but I have multiple error markers in eclipse. Now my query might not be legal, an answer of so (see comments) told me it wasn't possible to do what I intended to do in jpa. So, since I managed to do it anyway and I don't really have a clue why it works: my question is what are the underlying risks of using this :
query = "SELECT t FROM Thethread t LEFT JOIN FETCH t.threadVotes tv ON tv.user1=:currentUser ORDER BY t.datePosted DESC"
Under on:
JOIN FETCH expressions cannot be defined with an identification
Under :currentUser:
Input parameters can only be used in the WHERE clause or HAVING clause of a query.
I didn't manage to get the result I want without it. Which is :
Get the newest Thethread
Get only the current user vote in its collection.
If you know how to do that, please, be my guest.
The entities are as such :
public class Thethread implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long idthread;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "date_posted")
private Date datePosted;
private int downvotes;
private int upvotes;
// bi-directional many-to-one association to ThreadVote
#OneToMany(mappedBy = "thethread")
private List<ThreadVote> threadVotes;
}
public class ThreadVote implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id_votes_thread")
private int idVotesThread;
private int vote;
// bi-directional many-to-one association to Thethread
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "thread")
private Thethread thethread;
// bi-directional many-to-one association to User
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "from_user")
private User user1;
}
Your query is correct JPQL according to JPA 2.1 (JavaEE 7). Eclipselink supports it and other providers should too, if they support 2.1 version of JPA.
The operator ON with JOIN is new with this latest JPA version, it was not present in neither JPA 2.0 (JavaEE 6) nor in older JPA 1 versions.
Here is more info from EclipseLink wiki. The wiki states that Eclipselink implements ON operator and that it is in draft JPA 2.1. I checked also that it is also in final JPA 2.1 specification - and it is there.
In order to use your query, you just need to ensure that your environment/application server supports JPA 2.1 (e.g. application server should support Java EE 7, such as Glassfish 4 or WildFly 8+)
It is not a problem that your IDE (Eclipse) gives warnings until your query works. Eclipse probably does not support JPA 2.1 syntax or your project must be somehow configured to support JPA 2.1 and not older versions of JPA. Try to look into project properties, under project facets, and ensure you have JPA in version 2.1
What will actually happen if I provide a full set of annotations for JPA and JDO on data objects?
Can I then switch between them without touching the code? how can I switch what to aplay external configuration files? I know in the META-INF there are persistence.xml jdoconfig.xml but I do not understand the how to use them. (may be a link to a compressive explanation?)
Currently I got both files in place and the code below compiles Ok. I am interested in what goes under the hood to understand implications of this approach.
For vivid example:
#Entity
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
class B
{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Persistent
#Basic
private String name;
}
Platform: default setup of Google AppEngine 1.4 it uses DataNucleus Enhancer (version 1.1.4)
Although I have never try this it should work. This is the point of annotations: the do not affect the code unless they are used. JPA implementation uses its annotations, JDO uses others.
It turns out that the following example works when using mysql 5.x, however it doesn't when using an oracle 10g database.
Is there a way to define a unique identifier field that is independent of the database technology?
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private long id;
I have tested this in hibernate and the following exception occurs only when using Oracle:
org.hibernate.MappingException: Dialect does not support identity key generation
Using a database table is a portable way to generate identifiers.
The simplest way to use a table to generate identifiers is to specify TABLE as the generation strategy:
#Id
#GeneratedValue(strategy=GenerationType.TABLE)
#Column(name="id")
private long id;
The provider will create the default table if you're using schema generation; if not, you must specify an existing table:
#TableGenerator(name="InvTab",
table="ID_GEN",
pkColumnName="ID_NAME",
valueColumnName="ID_VAL",
pkColumnValue="INV_GEN")
#Id
#GeneratedValue(generator="InvTab")
#Column(name="id")
private long id;
http://www.oracle.com/technology/products/ias/toplink/jpa/howto/id-generation.html#table
I have researched using GenerationType.AUTO and it does appear to be the better option. It allows the JPA implementation to choose whatever is best for the data storage system you are using.