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.
Related
I'm currently using QueryDSL and I must add a WHERE clause to filter by a field which stores a huge amount of information (readable text) and it's created as a LOB field.
This is the field in my entity:
#Lob
#Column(name = "MY_FIELD", nullable = true)
private byte[] myField;
which is generated in this way in my "Q" class:
public final ArrayPath<byte[], Byte> myField = createArray("myField", byte[].class);
I can recover the information in this field without a problem. However, when I was trying to add the filtering clause, I realized the ArrayPath object doesn't have a like method, so I tried to do it in a different way.
I tried different approaches and I came up to this:
Expressions.predicate(Ops.LIKE, Expressions.stringPath("MY_FIELD"), Expressions.constant(stringValue));
The SQL code generated with the previous predicate is the following:
...
WHERE
MY_FIELD like '%?%' escape '!'
...
If I try to execute this SQL command directly in my database it perfectly works, it recovers the correct rows depending on the "?" param. However, my application doesn't recover any of them even though it's executing the very same SQL command.
Is there anything I'm missing? Could it be done in a different way?
Thank you very much in advance.
PS: I'm using SQL Server 2011.
By default a byte[] is mapped to an Array path. In case of a (C)LOB, you want to map it as String path instead. You can hint the code generator by specifying the QueryType:
#Lob
#QueryType(PropertyType.STRING)
#Column(name = "MY_FIELD", nullable = true)
private byte[] myField;
However, #Column(name = "MY_FIELD", nullable = true) seems to imply that you're querying JPA instead of plain SQL. Be aware that some JPA vendors may not support the like function for CLOBs.
After doing some refactoring moving some classes into different packages, I started seeing following error while querying the database with criteria builder:
java.lang.IllegalArgumentException: Parameter value [in.helpi.ironlegion.db.hibernate.entity.UserEntity#1863fc] did not match expected type [in.helpi.ironlegion.cerebro.db.hibernate.entity.UserEntity
If I change the package name back to in.helpi.ironlegion.cerebro.db.hibernate.entity it works just fine.
Update
I am able to properly fetch Individual entities. But when I go for querying entities having reference to other entity I get this error. For example:
public class CommunityAccessEntity extends BaseEnity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#ManyToOne
#JoinColumn(name = "UserEntity_id")
private UserEntity userEntity;
...
}
If I query it on user using criteria builder like:
query.select(root).where(criteriaBuilder.equal(root.get(CommunityAccessEntity_.userEntity), user)));
I get the above error.
Has somebody also faced similar issues..
You must have implemented Serializable interface in your entity classes as it is one of the thumb rules of entity class.
Java serialization is tightly coupled with class name and package name. Your data is stored in database with your old package entity. Now you changed the package name and system will not be able to find the records in DB with your new package. This is why when you restore the package, it works.
If you are using xml based configuration for hbm, please check hbm files whether new package have been updated in all the places.
I have been using Spring-Data-Mongo for a while now on a project without issues. In the last POM update, I started seeing the following exception in the log file:
WARN : 04 Aug 2014 13:55:24
org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty
- Customizing field name for id property not allowed! Custom name will not be considered!
It does not provide any clue as to where this issue arises so we are clueless. It is repeated several times as calls to the app are made. We did find the origin of this here: https://github.com/spring-projects/spring-data-mongodb/blob/11417144bd3574c35af06fde3a3c2e56a1dd5890/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java#L85
Any ideas?
Edit:
Added example class for those interested:
#Document(collection="Account")
public class Account {
....
#Id
private String id = null;
....
}
I listened to the nice comments above and put a breakpoint on . I quickly found out that one of my classes was using the following:
#Id
private String Id;
Note the Id instead of id. Once changed, the warning disapearind.
I fully support the suggestion to have spring-data provide a proper report rather than simply a meaningless warning.
My thought is that you are trying to use #Id with #Field on the same field. This is not allowed since the id field in Mongo must be called _id. Anything further is hard to tell since you didn't post your code.
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!
I have made an application using Java/Hibernate/MySQL, but whenever I try running it, it complains that the table for one of my entity classes has not been created.
After some trial and error, I have come to the conclusion that the problem is coming from the way I am mapping embedded components, but I do not know how I may fix it.
Here are the relevant bits of code:
#Entity
public class Feed {
... //Definition of some properties
#Embedded
#AttributeOverrides( {
#AttributeOverride(name = "type", column = #Column(name = "title-type")),
#AttributeOverride(name = "mode", column = #Column(name = "title-mode")),
#AttributeOverride(name = "value", column = #Column(name = "title-value")) })
public Content getTitle() { ... }
...
}
#Embeddable
public class Content {
... // There are three properties with bean syntax
// without any persistence annotation.
}
Does anyone know why Hibernate fails to create the table for the class Feed? And how I may correct it?
Thank you in advance.
Edit: Finally, I have understood; the fact that I used "-" characters in my columns was to blame. I replaced these with underscores and all is well again.
Thank you very much for your help.
Excuse me for the lack of updates, but I have finally been able to log the necessary debug information.
The names I had chosen for my columns were at fault; I would guess that the "-" is reserved for arithmetic operations and that it should not be in a column name.
I replaced them with underscores, and that problem is solved.
Thank you very much for your help.
you sure you Override all of your Embeddable Class Properties(Although it must work when you don't override all)
When I compare your code with mine I have seen no difference (I just implement Serializable for my class)
can you write what exception it throw?