Help needed for JPA/hibernate - problems with creating entity classes - java

I started creating JPA/hibernate mappings for a legacy database based on Oracle. At one (early...) point I have a many-to-many relation between to tables (FOO, BAR, join table with extra fields: FOO_BAR). So I defined three entities, created an embeddable Id class for the join table, strictly following some examples from a good (?!) book.
I can select a Foo but whenever I try to read the related Bars from the result set, I get wither an "SQLException: No data found" or a "SQLException: General error (S1000)". I can switch between the two by just changing some java types of the entity beans...
The log contains the following line n case of the "No data found" error:
INFO org.hibernate.type.LongType - could not read column value from result set: Foo1_2_; No data found
The columns FOO_ID and BAR_ID are defined as NUMBER(22). I tried Long types first, this resulted in "No data found", Double led to "General error". Then I read somewhere that the standard mapping for NUMBER is BigDecimal (-> "General Error"), I tried BigInteger instead (-> "No data found").
I'm lost.
When I take the sql statement from the logs and use it with "native" jdbc, ... it works fine.
PreparedStatement prep = con.prepareStatement(sqlQueryFromHibernateLogs);
prep.setBigDecimal(1, new BigDecimal(1));
ResultSet rs = prep.executeeQuery(); // result set has the correct values...
Any help, suggestions, pointers to helpful resources are highly appreciated. Oh, one final thing to mention: I'm 'forced' to use the JdbcOdbc bridge. It's really a legacy system...
My select statement goes like this:
List<Foo> foos = em.createQuery("select f from Foo f order by f.name").getResultList();
Edit
Versions - I'm bound to the hibernate libraries that are shipped with the play framework (1.0.3.2). The hibernate3.jar has no useful version information (nothing in Manifest, Version#getVersionString() says [WORKING]), the other hibernatexxx jars report as 3.1.0.GA (validator) or 3.4.0.GA (entitymanager).
Edit 2
I've reduced the classes to the absolute minimum with the errors still present. This is what I did:
Foo.java
#Entitiy #Table(name="FOO")
public class Foo {
#Id #Column(name="FOO_ID")
private BigInteger fooId;
Foo(){}
#OneToMany(mappedBy="foo")
private Set<FooBar> fooBars = new HashSet<FooBar>();
}
Bar.java
#Entitiy #Table(name="BAR")
public class Bar {
#Id #Column(name="BAR_ID")
private BigInteger fooId;
Bar(){}
#OneToMany(mappedBy="bar")
private Set<FooBar> fooBars = new HashSet<FooBar>();
}
FooBar.java
#Entitiy #Table(name="FOOBAR")
public class FooBar {
#Embeddable
public static class Id implements Serializable {
#Column(name="FOO_ID")
private BigInteger fooId;
#Column(name="BAR_ID")
private BigInteger barId;
Id() {}
// implementations of hashcode and equals
}
#Embedded
private Id id = new Id();
#ManytoOne #JoinColumn(name = "FOO_ID", insertable=false, updatable=false)
private Foo foo;
#ManytoOne #JoinColumn(name = "BAR_ID", insertable=false, updatable=false)
private Bar bar;
FooBar(){}
}
FOO_ID and BAR_ID are defined as NUMBER(22) on the the Oracle database. The above example leads to the "No data found" error, replacing BigInteger with Long results in the "General Error". And sending the very same SQL expression over the bridge gives a correct result set...

Found the solution or - at least - found out, that the approach can't work with the given tools (hibernate, JdbcOdbc bridge):
Some more googeling finally sent me to this (german) page, where someone had a similiar problem: a No data found SQLException while working with the bridge. Someone else answered, that the code is correct but it is a problem with the JdbcOdbc bridge which does not support reading a result set more than once. I can't clarify, if I have the same problem here, but I'm pretty sure that it is this or a similiar case and the problem is related to using the bridge with hibernate/JPA.
For my very special case: I solved the problem by ignoring it after ... finally finding out that I can use the oracle oci driver. (sigh)
Thanks to everyone who tried to follow my posting and thought about a solution!

Related

Multiple levels of cascading persists in Ebean

I have a model class which defines a list of children that are models of the same class. Persisting a new object with some initial children works fine, but when I have two or more levels of children Ebean does not seem to be able to handle it well. This seemed unexpected so I'm worried I made a mistake. At the same time I couldn't find any examples or mentions about multiple level persist cascades so my questions are:
Is there an error in my code, Is this even a supported feature or did I find a bug?
My model class:
#Entity
public class TestEntity extends Model {
#Id
private int id;
private String text;
#ManyToOne
private TestEntity parentEntity;
#OneToMany(cascade = CascadeType.ALL)
private List<TestEntity> childEntities;
...
}
My program:
TestEntity grandparent = new TestEntity();
grandparent.setText("grandparent");
TestEntity parent = new TestEntity();
parent.setText("parent");
TestEntity child = new TestEntity();
child.setText("child");
grandparent.setChildEntities(Collections.singletonList(parent));
parent.setChildEntities(Collections.singletonList(child));
grandparent.save();
I added logging for the sql statements and it is evident that the third insert didn't get the correct value for parent_entity_id. That row fails due to 0 not being a valid foreign key and the batch is reverted.
insert into test_entity (text, parent_entity_id) values ('grandparent',null);
insert into test_entity (text, parent_entity_id) values ('parent',1);
insert into test_entity (text, parent_entity_id) values ('child',0);
I'm using Play framework 2.7.3 with the ebean plugin version 5.0.2 and Ebean version 11.39
This is indeed a supported feature and the code snippet above is expected to persist all three entities.
There was a unit test added to verify that this is working correctly in the latest version of ebean.
In ebean 11.39 which is currently the latest supported by play framework the test fails. An easy workaround when using that version is to use Long instead of primitive int as ID for the models.
While not an answer to this specific question, it is good to be aware that these same symptoms also appear if the collections are set without using setters enhanced by ebean. I had some trouble using public fields and play enhancer .

Read javax annotations with custom doclet

I got a bunch of DTO's which are not commented at all. However, there are comments in the SQL-Database. I can get these comments by sending a query and then retrieving the ResultSet.
My task is to create a javadoc-API (as HTML) with the comments from the SQL-Database in order to make the codebase better understandable.
After asking about this task already HERE, I tried to looked into creating my own doclet. I then wrote my own doclet by rewriting the Standard-, Abstract- and HtmlDoclet from Java.Tools. My results are working fine and I can create javadoc html pages WITH the comments from the database.
HOWEVER its a massive hack imho. There are two main tasks that need to be done in order to get the Database comments.
know the table name
know the column name
How it should be done: (which is what I want to ask - How do I implement it like this?)
For 1. : Find the #Table annotation. Read name = "tablename".
For 2. : For each variable:
Is there a #Column annotation ? return "columnName" : return ""
How I do it right now:
For 1. : I read the RootDoc.name() variable and then read the String char by char. Find a capital letter. Insert '_'. And at the end, turn everything .toUpperCase(). So "testFile" turns into "TEST_FILE".
This sometimes does not work. If you read carefully in the example class. Its name is "SaklTAdrkla" but the Databasetable name is SAKL_T_ADRKLAS. Parsing the name from RootDoc.name() would result in "SAKL_T_ADRKLA" which is missing the character 'S' at the end, therefore it wont find the table in the database.
For 2. : I get all Fields from the ClassDoc. I then parse Field.name() the same way I parsed the RootDoc.name() variable.
This wont work for the same reason as 1.; but also because some fieldnames are not the same as their mapped names. In the example Class - field sakgTAklgrpAklAkgid is mapped in the database as AKL_AKGID
I am able to find the Annotation itselfe by calling FieldDoc.annotations(). But thats ONLY the annotation without the String (name = "xyz") which is the most important part for me!
I have found the Jax-Doclet, which can parse the javax annotations. However after downloading the jar-source file and implementing the java files, there are numerous dependency issues which are not resolvable because the referenced classes no longer exist in java 8 tools.jar.
Is there another solution, that is capable of reading the javax annotations?
Can I implement something into my doclet so it can read the javax annotations?
Edit:
I found out you can call .elementValues() on the AnnotationDesc class which I can get from FieldDoc.annotations(). However I always get a com.sun.jdi.ClassNotLoadedException Type has not been loaded occurred while retrieving component type of array. To fix it I manually load the classes AnnotationDesc and AnnotationDesc.ElementValuePair by calling Class.forName(). However now the Array with the elementValuePairs is empty..?
Example class:
/**
* The persistent class for the SAKL_T_ADRKLAS database table.
*/
#Entity
#IdClass(SaklTAdrklaPK.class)
#Table(name = "SAKL_T_ADRKLAS")
#NamedQuery(name = "SaklTAdrkla.findAll", query = "SELECT s FROM SaklTAdrkla s")
public class SaklTAdrkla implements Serializable, IModelEntity {
private static final long serialVersionUID = 1L;
#Id #Column(name = "AKL_AKLID") private String aklAklid;
#Id
// uni-directional many-to-one association to SakgTAklgrp
#JsonBackReference(value = "sakgTAklgrpAklAkgid") #ManyToOne #JoinColumn(name = "AKL_AKGID") private SakgTAklgrp sakgTAklgrpAklAkgid;
#Temporal(TemporalType.TIMESTAMP) #Column(name = "AKL_AEND") private Date aklAend;
#Column(name = "AKL_DEFLT") private BigDecimal aklDeflt;
#Column(name = "AKL_SPERRE") private BigDecimal aklSperre;
#Column(name = "AKL_T_BEZ") private String aklTBez;
#Column(name = "AKL_USRID") private String aklUsrid;
public SaklTAdrkla() {
}
It took me quite a while to figure this out now, but I finnally did.
The Problem was that my doclet could find all the annotations, which it displayed in the console as errors.
error: cannot find symbol #Column(name = "TST_USER") private
String tstUser;
What I also found was this message in the lot of errors that got thrown:
error: package javax.persistence does not exist import
javax.persistence.*;
So I imported javax.persistance.jar into my project.
I also added com.fasterxml.jaxkson.annotations.jar into the project since it would also not work without it.
Surprise Surprise! IT WORKS!
I can get all the annotations and annotation values by using annotation.elementValues().
I no longer get an empty Array nor do I get an ClassNotLoadedException.

Better approach to maintain audit of few entities

I have one Hibernate entity with following structure:
#Entity
public class A
{
private Integer id;
private String name;
#OneToMany
private List<B> bList;
}
#Entity
public class B
{
private Integer id;
#OneToMany
private List<C> cList;
}
Now I wants to maintain history on entity "A", with information like :
historyDate | fieldsChanged | updatedBy | createdBy
In fieldsChanged column i want the name of the columns of entity A and if any changes applied in entity B or C.
Now I have googled and find few of the following ways to achieve these :
Writer trigger on the DB
Own Java logic to maintain history table
Using Hibernate Envers
Now I am not sure what should be the better approach from above or anything else.
Any suggestion would be highly appreciated...
Envers uses a different auditing scheme - storing the full content of an entity for each change. The answer really depends on what you need.
is the right choice if the DB is accessed directly from various systems, written in various languages.
is best if you need to have the structure that you wrote about
gives you auditing, but in a different format.

Best way to prevent unique constraint violations with JPA

I have an Keyword and a KeywordType as entities. There are lots of keywords of few types. When trying to persist the second keyword of a type, the unique constraint is violated and the transaction is rolled back. Searching SO i found several possibilies (some of them from different contexts, so I'm not sure of their validity here) - this post and this post advise catching the Exception which would be of no use to me as I end up where I started and still need to somehow persist the keyword.
Same applies to locking proposed for a different situaltion here Custom insert statements as proposed in this and this posts wouldn't work proper I guess, since I'm using Oracle and not MySQL and woulnd like to tie the implementation to Hibernate. A different workaround would be trying to retrieve the type first in the code generating the keywords, and set it on the keyword if found or create a new one if not.
So, what would be the best - most robust, portable (for different databases and persistence providers) and sane approach here?
Thank you.
The involved entities:
public class Keyword {
#Id
#GeneratedValue
private long id;
#Column(name = "VALUE")
private String value;
#ManyToOne
#JoinColumn(name = "TYPE_ID")
private KeywordType type;
...
}
and
#Entity
#Table(uniqueConstraints = {#UniqueConstraint(columnNames = { "TYPE" }) })
public class KeywordType {
#Id
#GeneratedValue
private long id;
#Column(name = "TYPE")
private String type;
...
}
Your last solution is the right one, IMO. Search for the keyword type, and if not found, create it.
Catching the exception is not a good option because
it's hard to know which exception to catch and make your code portable across JPA and DB engines
The JPA engine will be in an undetermined state after such an exception, and you should always rollback in this case.
Note however that with this technique, you might still have two transactions searching for the same type in parallel, and then try to insert it in parallel. One of the transaction will rollback, but it will be much less frequent.
If you're using EJB 3.1 and you don't mind serializing this operation, a singleton bean using container managed concurrency can solve the problem.
#Singleton
#ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class KeywordTypeManager
{
#Lock(LockType.WRITE)
public void upsert(KeywordType keywordType)
{
// Only one thread can execute this at a time.
// Your implementation here:
// ...
}
#Inject
private KeywordTypeDao keywordTypeDao;
}
I would go for this option:
A different workaround would be trying
to retrieve the type first in the code
generating the keywords, and set it on
the keyword if found or create a new
one if not.

Lazily loading a clob in hibernate

There's a lot one can find about this googling a bit but I haven't quite found a workable solution to this problem.
Basically what I have is a big CLOB on a particular class that I want to have loaded on demand. The naive way to do this would be:
class MyType {
// ...
#Basic(fetch=FetchType.LAZY)
#Lob
public String getBlob() {
return blob;
}
}
That doesn't work though, apparently due to the fact I'm using oracle drivers, i.e. Lob objects aren't treated as simple handles but are always loaded. Or so I've been led to believe from my forays. There is one solution that uses special instrumentation for lazy property loading, but as the Hibernate docs seem to suggest they're less than interested in making that work correctly, so I'd rather not go that route. Especially with having to run an extra compile pass and all.
So the next solution I had envisioned was separating out this object to another type and defining an association. Unfortunately, while the docs give conflicting information, it's apparent to me that lazy loading doesn't work on OneToOne associations with shared primary key. I'd set one side of the association as ManyToOne, but I'm not quite sure how to do this when there's a shared primary key.
So can anybody suggest the best way to go about this?
According to this only PostgreSQL implements Blob as really lazy. So the best solution is to move the blob to another table. Do you have to use a shared primary key? Why don't you do something like this:
public class MyBlobWrapper {
#Id
public Long getId() {
return id;
}
#Lob
public String getBlob() {
return blob;
}
#OneToOne(fetch=FetchType.LAZY,optional=false)
public MyClass getParent() {
return parent;
}
}
Instead of doing equilibristics with hibernate annotations, one may just try converting the field from String into Clob (or Blob):
#Lob
#Basic(fetch=FetchType.LAZY)
#Column(name = "FIELD_COLUMN")
public Clob getFieldClob() {
return fieldClob;
}
public void setFieldClob(Clob fieldClob) {
this.fieldClob = fieldClob;
}
#Transient
public String getField()
{
if (this.getFieldClob()==null){
return null;
}
try {
return MyOwnUtils.readStream(this.getFieldClob().getCharacterStream());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void setField(String field)
{
this.fieldClob = Hibernate.createClob(field);
}
Worked for me (the field started to load lazily, on Oracle).
Since you appear to be using Hibernate I wonder if your problem is related to the following Hibernate feature:
Using Lazy Properties Fetching
Hibernate3 supports the lazy fetching of individual properties. This
optimization technique is also known as fetch groups. Please note that
this is mostly a marketing feature; optimizing row reads is much more
important than optimization of column reads. However, only loading
some properties of a class could be useful in extreme cases. For
example, when legacy tables have hundreds of columns and the data
model cannot be improved.
Lazy property loading requires buildtime bytecode instrumentation. If
your persistent classes are not enhanced, Hibernate will ignore lazy
property settings and return to immediate fetching.
See Bytecode Instrumentation for Hibernate Using Maven.
Old post, but only one that helped me, thanks to #TadeuszKopec answer.
Looks like it is hard to do lazy loading of blob with JPA. I tried #OneToOne association, but it complicates more than help.
I just moved the bytes to another class, with no association with MyClass (parent. Same table, same id):
#Entity
#Table(name="MyTable")
public class MyBlobWrapper{
#Id
#Column(name = "id") // id of MyTable, same as MyClass
private Long id;
#Lob
private byte[] bytes;
}
#Entity
#Table(name="MyTable")
public class MyClass{
#Id
#Column(name = "id")
private Long id;
// other fields .....
}
Just remember to flush parent, before saving the blob:
em.persist(parent);
em.flush();
em.merge(new MyBlobWrapper(parent_id,new byte[1000]));
Now I can load the pdf alone:
String query1 = " select PDF from MyBlobWrapper PDF where PDF.id = :id";
I am just beginner with JPA, hope that helps.

Categories

Resources