How can I use capitalized property in a hibernate entity - java

In our product we use auto generated hibernate entities to be able to link a customizable Database scheme to our server software. The entity names and property names are taken from the data base. Especially, the property names can usually not be changed as they also are used in user code unrelated to the hibernate data layer (e.g. python scripts)
Some of these property names are capitalized, which seems to cause some problems. HQL statements using those property names fail with an Exception, e.g.:
org.hibernate.QueryException: could not resolve property List_id
at org.hibernate.QueryException.generateQueryException(QueryException.java:137)
at org.hibernate.QueryException.wrapWithQueryString(QueryException.java:120)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:234)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:158)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:126)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:88)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:190)
Some code snippet for the example Exception:
#Entity(name = "ListItem")
#Table(name = "LIST_ITEM")
public class ListItem
extends HibernatePojoClass
{
private String List_id = "";
#Column(name = "`LIST_ID`", length = 8)
public String getList_id() {
return List_id;
}
public void setList_id(String List_id) {
this.List_id = List_id;
}
...
and the HQL statement:
select li.id, li.List_id from ListItem li
The exception occurs when hibernate tries to transform the hql statement to a sql statement.
Why does this happen?
It seems that when I use li.list_id in the hql statement, the property is resolved (while this leads to another error); can I prevent this implicit "capitalization change" somehow?

if you use
#Column(name = "`LIST_ID`", length = 8)
public String getList_id() {
return List_id;
}
you should refer that property as list_id in HQL, of course.
Hibernate can use a naming strategy to generate column names. ImprovedNamingStrategy from Hibernate 4 will convert column name to the lower case, even if you specify it. I am not sure about the quotes, but for this:
#Column(name = "LIST_ID", length = 8)
public String getList_id() {
return List_id;
}
using ImprovedNamingStrategy you will have list_id column name.
You can try to use your own naming strategy to generate correct column names.

JPA has 2 basic access modes: property access and field access.
Property access requires you to adhere to the Java Beans convention which means you need field name that starts with a lower case character and a corresponding getter/setter which has the same character in upper case, i.e. field listId would require a getter getListId().
Thus you'd need to use field access in order to have Hibernate use the field name as it is. Another advantage of using field access on an entity's id would be that you'd not need to do any lazy loading in order to just get the id - which wouldn't be possible with property access in Hibernate.
For more information have a look at sections 2.2 and 2.3 of the JPA specification.
A final word of advice though: as already stated multiple times in my comments you should try and stick with the Java code conventions. Some advantages of doing so:
It'll be easier to communicate with others such as people here on SO (e.g. a name starting with a capital letter normally is assumed to be a class name).
You'll have less problems with libraries in the Java eco system since most of them use the same conventions or are based on them (e.g. JavaBeans, JavaEL, etc.)
It'll be easier to spot errors, e.g. when using a class rather than a field or variable etc.
You'll be less dependent on IDE features like code coloring, error highlighting etc.

Related

Need to access real column name from Hibernate Implicit naming strategy

I'm implementing a custom hibernate JPA naming strategy in a Hibernate 5.4.32 model.
The implicit naming strategy is setup to convert camelCase property names to snake_case. Thus a property called myKey will turn into a column called my_key. This part works correct, and all columns have the correct names and all JPA interactions work.
However I have an issue with generating index names. In that Strategy interface there is a method:
public Identifier determineIndexName(ImplicitIndexNameSource source)
The parameter "source" passed in that method is ambiguous in how it handles the column names that are passed to the method.
For instance if I create the following entity
#Table(schema = LoaderModel.SCHEMA, name = "main_entity",indexes = {#Index(columnList = "other_id,myKey")} )
#Entity
public class MainEntity{
#ManyToOne
OtherEntity other;
#Column(columnDefinition = "text")
String myKey;
}
This entity creates a table with two columns, other_id, and my_key. But notice how in the #Index I must use the property name "myKey", even though for the ManyToOne column I have to use the actual column name: "other_id". This is in line with what I see in the determineIndexName method:
#Override
public Identifier determineIndexName(ImplicitIndexNameSource source) {
String name = "ix_"+source.getTableName().getText()+"_"+String.join("_",source.getColumnNames().stream().map(Identifier::getText).collect(Collectors.toList()));
//The name here is: ix_main_entity_other_id_myKey
//instead of the desired: ix_main_entity_other_id_my_key
return Identifier.toIdentifier(name);
}
I can't find any properties of the ImplicitIndexNameSource object that allow me to detect the actual column names of the properties in the index. In addition I'm not sure why it passes the column name for entity references, but the property name for entity properties.
I'm guessing some portion of this weird ambiguity is intended, but If I could simply get access to the real column name then I could write the code to handle it.
Note: Manually configuring the column name "my_key" for the myKey property seems to fix this issue, but that defeats the purpose of creating the naming strategy (which would be applied to the entire model), so that's not a valid fix.

Is there a way to specify collection name for Java bean being stored into RavenDB?

I'm using RavenDB with JAVA ravendb-jvm-client for my app, where existing collection names does'n reflect 'User.java' -> 'Users' convention. Is there a way how to specifiy correct collection name for java beans so java client use this instead of automatic convention? Something similar to #Table annotation for JPA.
I know I can specify collection name for example in queries e.g. session.query(User.class, Query.collection("custUsers")); ... but its very verbose to specify it repeatedly.
You need to use FindCollectionName convention of the DocumentStore.
In Java, you can use setFindCollectionName of the conventions.
From https://ravendb.net/docs/article-page/4.1/java/client-api/session/configuration/how-to-customize-collection-assignment-for-entities :
By default a collection name is pluralized form of a name of an entity type. For example objects of type Category will belong to Categories collection. However if your intention is to classify them as ProductGroups use the following code:
store.getConventions().setFindCollectionName(clazz -> {
if (Category.class.isAssignableFrom(clazz)) {
return "ProductGroups";
}
return DocumentConventions.defaultGetCollectionName(clazz);
});
Documentation on conventions in general: https://ravendb.net/docs/article-page/4.1/java/client-api/configuration/identifier-generation/global

All my fields have a prefix. How can I tell that to hibernate?

E.g. I have:
#Column(name = "username")
private String m_username;
Note that the #Column annotation only affects the database column name.
Hibernate still thinks the name of the property is 'm_username'.
How can I tell Hibernate that the property name is just 'username'?
Please tell me there is a way to do this...
Edit: I removed the #AccessType annotation in my code example, as it is not relevant for this question.
Update: After switching everything to field access, this exception happens:
org.hibernate.QueryException: could not resolve property: username of: mypackage.model.User
It happens here:
Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.eq("username", username));
User result = (User) criteria.uniqueResult();
And the reason is most likely that hibernate only 'knows' of a property called 'm_username', while I think of it and program against a property named 'username'. Also note that my getters/setters are called: "getUsername()" and "setUsername(String value)" (automatically generated).
why do you use AccessType.PROPERTY?
remove it and it is accessed by 'field'.
do not mix field and property annotations. stick to one or the other.
As per my knowledge we can't* .
There is still one issue with the way hibernate looks up the methods
for a given property name.
Suppose you have a property with a name like "mProperty" (first
lowercase, second uppercase, rest doesn't matter). Not the accessor
methods in the source code will be getMProperty and setMProperty.
The way BasicPropertyAccessor.getterMethod is implemented in that way .
I found one lead here: https://forum.hibernate.org/viewtopic.php?f=1&t=943110
But the thread is 7 years old and I don't know how to apply this to annotation based configuration (I don't have an xml configuration file).

Cannot use QueryBuilder with column name = "`GROUP`" in ormlite

The code
QueryBuilder<MyClass, String> builder = mnDao.queryBuilder();
builder.where().eq("`GROUP`", someGroup));
throws exception
"Unknown column name '`GROUP`' in table MyClassTable"
I've tried to use escapeColumnName() from UpdateBuilder but the result is the same.
I could use raw queries, but they are not safe, because they don't provide placeholders.
The database is H2.
It looks like a bug in ormlite.
Any suggestions?
The right answer is to not do any quoting at all. ORMLite automatically quotes all of the field and table names. This is very database dependent for sure but H2 is my primary test database so I'm sure it works well in all cases that I know about for it. As to why it didn't work for you I can't comment on. If you post the query generated by ORMLite I may see the problem.
The pattern that is recommended is to define any columns that are to be used in queries as public strings. For example:
protected static class Reserved {
public static final String FIELD_NAME_GROUP = "group";
...
#DatabaseField(columnName = FIELD_NAME_GROUP)
String group;
...
}
Then when you query, you then use the FIELD_NAME_GROUP without any quoting and do:
QueryBuilder<Reserved, Integer> sb = dao.queryBuilder();
sb.where().eq(Reserved.FIELD_NAME_GROUP, "something");
...
I have good coverage of this in a couple of unit tests. See the following test that I run across all of my database types that are supported. Look for testCreateReserverdTable() and testCreateReserverdFields() methods and their associated classes in the JdbcBaseDaoImplTest.java unit test.
I don't know ormlite, and it ought to have a way of quoting a name in a way that allows you to use odd table names, but the easiest fix for this sort of thing is to avoid using sql keywords as table names or column names.
If you can change the table name to something sql doesn't use for something else, it'll probably just work.

JPA - Setting entity class property from calculated column?

I'm just getting to grips with JPA in a simple Java web app running on Glassfish 3 (Persistence provider is EclipseLink). So far, I'm really liking it (bugs in netbeans/glassfish interaction aside) but there's a thing that I want to be able to do that I'm not sure how to do.
I've got an entity class (Article) that's mapped to a database table (article). I'm trying to do a query on the database that returns a calculated column, but I can't figure out how to set up a property of the Article class so that the property gets filled by the column value when I call the query.
If I do a regular "select id,title,body from article" query, I get a list of Article objects fine, with the id, title and body properties filled. This works fine.
However, if I do the below:
Query q = em.createNativeQuery("select id,title,shorttitle,datestamp,body,true as published, ts_headline(body,q,'ShortWord=0') as headline, type from articles,to_tsquery('english',?) as q where idxfti ## q order by ts_rank(idxfti,q) desc",Article.class);
(this is a fulltext search using tsearch2 on Postgres - it's a db-specific function, so I'm using a NativeQuery)
You can see I'm fetching a calculated column, called headline. How do I add a headline property to my Article class so that it gets populated by this query?
So far, I've tried setting it to be #Transient, but that just ends up with it being null all the time.
There are probably no good ways to do it, only manually:
Object[] r = (Object[]) em.createNativeQuery(
"select id,title,shorttitle,datestamp,body,true as published, ts_headline(body,q,'ShortWord=0') as headline, type from articles,to_tsquery('english',?) as q where idxfti ## q order by ts_rank(idxfti,q) desc","ArticleWithHeadline")
.setParameter(...).getSingleResult();
Article a = (Article) r[0];
a.setHeadline((String) r[1]);
-
#Entity
#SqlResultSetMapping(
name = "ArticleWithHeadline",
entities = #EntityResult(entityClass = Article.class),
columns = #ColumnResult(name = "HEADLINE"))
public class Article {
#Transient
private String headline;
...
}
AFAIK, JPA doesn't offer standardized support for calculated attributes. With Hibernate, one would use a Formula but EclipseLink doesn't have a direct equivalent. James Sutherland made some suggestions in Re: Virtual columns (#Formula of Hibernate) though:
There is no direct equivalent (please
log an enhancement), but depending on
what you want to do, there are ways to
accomplish the same thing.
EclipseLink defines a
TransformationMapping which can map a
computed value from multiple field
values, or access the database.
You can override the SQL for any CRUD
operation for a class using its
descriptor's DescriptorQueryManager.
You could define a VIEW on your
database that performs the function
and map your Entity to the view
instead of the table.
You can also perform minor
translations using Converters or
property get/set methods.
Also have a look at the enhancement request that has a solution using a DescriptorEventListener in the comments.
All this is non standard JPA of course.

Categories

Resources