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.
Related
I wanted to perform the Spring JPA repository where wanted to apply the and operation among 2 columns where one column cloud have multiple values in it.
SQL query for the same:
select * from table_name where col1='col1_val' and col2 IN
('col2_val_a','col2_val_b','col2_val_c');
I know that for and operation I can extend the JpaRepository and create the method with like this for:
List<MyPoJoObject> findByCol1AndCol2(String col1_val,String col2_val);
and for IN operation we can use : findByCol2In(Collection<String> col2_val)
But i did not know how i can club both the mentioned JPA default method into one, as per my sql statement mentioned before.
You can use the following method named:
List<MyPoJoObject> findByCol1AndCol2In(String col1_val, Collection<String> col2_val);
On this link repository-query-keywords you can find repository query keywords that you can use and combine them as well.
You can certainly combined both into one method.
List<MyPoJoObject> findByCol1AndCol2In(String col1_val,String[] col2_val);
Try this. I am not sure if it will accept Collection<String>. I will try that and update the answer.
HTH.
If you want to perform this logic for more than two columns then your method name becomes verbose.
Instead of stuck with Spring naming why can't you write your own JPA query.
Example:
#Query("select pojo from MyPoJoObject as pojo where pojo.col1 = :col1_val and pojo.col2 in :col2_val")
List<MyPoJoObject> findByColumns(String col1_val, List<String> col2_val);
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.
With Spring JPA is there an easy way to use native queries but maintaining database independence, for example by using the query which fits best?
At the moment I do this by checking the currently set Dialect from the Environment and call the proper method of my Repository:
public Foo fetchFoo() {
if (POSTGRES_DIALECT.equals(env.getRequiredProperty("hibernate.dialect"))) {
return repo.postgresOptimizedGetFoo();
}
return repo.getFoo();
}
This works but I have the feeling that there is a better way or that I am missing something. Especially because (Spring) JPA allows it to use native queries quite easily but that breaks one of its big advantages: database independence.
As per my understanding, this can be achieved simply by using #Transactional(readOnly=false) and then instead of calling session.createQuery, one can use session.createSQLQuery, as provided in this example.
Your sql can be any of your native query.
Hope this works for you. :)
#Override
#Transactional(readOnly = false)
public Long getSeqVal() {
Session session = entityManager.unwrap(Session.class);
String sql = "SELECT nextval('seqName')";
Query query = session.createSQLQuery(sql);
BigInteger big = (BigInteger) query.list().get(0);
return big.longValue();
}
This is just an idea: I do not know whether it works or not:
My idea would be having subinterfaces, one normal Spring-Data-JPA-interface with all methods for one entiy (without native query hints). Than I would crate a subinterface for every database, that "override" the database specific native statements. (This intrface would be empty if there are no DB specific statements). Then I would try configure Spring-JPA with some profiles to load the right specific interface (for example by a class-name or package-name-pattern)
This seems like a way to complicated way to get queries to work.
If you really want to use optimized queries make it at least transparant for your code. I suggest using named queries and create an orm.xml per database (much like Spring Boot uses to load the schema.xml for a different database).
In your code you can simply do
public interface YourRepository extends JpaRepository<YourEntity, Long> {
List<YourEntity> yourQueryMethod();
}
This will look for a named query with the name YourEntity.yourQueryMethod. Now in your orm.xml add the named query (the default one and in another one the optimized one).
Then you need to configure your LocalContainerEntityManagerFactory to load the specific one needed. Assuming you have a property defining which database you use, lets name it database.type you could do something like the following
<bean class="LocalContainerEntityManagerFactoryBean">
<property name="mappingResources" value="classpath:META-INF/orm-${database.type}.xml" />
... other config ...
</bean>
This way you can keep your code clean of the if/then/else construct and apply where needed. Cleans your code nicely imho.
While working on some java projects i've saw some sort of SQL repository.
The idea was to place all queries in one(or few) xml files and retrieve them when needed by name. Something like this:
String sql = getSQLRepository().getSQL("SELECT_ALL_ROWS", params)
String sql2 = getSQLRepository().getSQL("SELECT_ROWS_WITH_COND", params)
In my current Grails project i have a lot of HQL queries in dozens of classes and it's hard to track them all. It seems that HQL repository would be very nice solution.
So could anyone tell if some sort of SQL\HQL repository implementation allready present or there are better solutions present ?
Have a look at Mapping Queries in Hibernate reference.
After we started use the http://source.mysema.com/display/querydsl there is no need to think about text queries and how to manage them.
I'd recommend you to use the good old properties files. You can put them into your classpath and then use as following:
Properties sql = new Properties();
properties.load(getClass().getResourceAsStream("sql.properties"));
////////
String query = sql.get("SELECT_ALL_ROWS");
I'm sorry, and it doesn't relate to Hibernate, but when I worked with iBatis, - there are the situation as you are writing about exactly. A few xml (partially generated itself) files, containing SQL queries, which was easily to use in DAO
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.