How to create/call a sql view in Hibernate - java

Here is the view created in document.hbm.xml
<database-object>
<create><![CDATA[CREATE VIEW docView
AS
SELECT * from document;
GO]]></create>
<drop>DROP VIEW docView</drop>
<dialect-scope name='org.hibernate.dialect.SQLServerDialect' />
</database-object>
Now how to call this view in my method
Tried calling like this
Session session = sessFactory.openSession();
Query query = session.createSQLQuery("docView");
List<?> list = query.list();
Ended up with
Caused by: java.sql.SQLException: The request for procedure 'docView' failed because 'docView' is a view object.
at net.sourceforge.jtds.jdbc.SQLDiagnostic.addDiagnostic(SQLDiagnostic.java:368)
at net.sourceforge.jtds.jdbc.TdsCore.tdsErrorToken(TdsCore.java:2820)
at net.sourceforge.jtds.jdbc.TdsCore.nextToken(TdsCore.java:2258)
at net.sourceforge.jtds.jdbc.TdsCore.getMoreResults(TdsCore.java:632)
Any Idea or any otherway to call sql view in hibernate?
In short is there a way like Is there any way to call view just like a stored procedure??, without creating a new entity??

You can work with a DB view as if it were a regular entity table. Define an entity class, either with the #Entity annotation or an equivalent XML and an arbitrary subset of the view's columns as fields.
An important point is that you should not change the values in the entity, as the view is read-only.
EDIT: I am not aware of a way to use a view like a stored procedure. If the purpose of your stored procedure is querying over multiple entities as implied in your comment, you could either:
make the view 'broad' enough to contain all the necessary attributes of the needed entities
relate to the relevant entities using foreign key columns in the view and regular #*To* annotations for the entity that is mapped to the view.
I am afraid this does not bring you very far, since you still have to either use native SQL or define an entity.

Create an entity to map it to your view, then use it for querying your view
#Entity
#Table(name = "docView")
public class DocView {
// Put all fields that you use in your view
documentField1;
documentField2;
.
.
}
Then you could make your query like this:
Session session = sessFactory.openSession();
Query query = session.createSQLQuery("from DocView");
List<?> list = query.list();

Hibernate view is not a named query. You can create the view in you DB and then create the entity pojos with the view in mind. Hibernate will treat these entities as mapped to a view and then you can perform you operation as you normally do for a hibernate entity.
You are writing a create DDL query to create the view which will only be called if the hbm2ddl property is set correctly.
Create view is a DDl query and it does not return a list of values. You cannot call the create view query as if its a named sql query
Once the view is created you can write your named sql queries which can retrieve data from the view. For that all you need is a POJO which maps to the view and the named query to get the data.

Related

Spring projection with entity inside

I need in metainfo for entity (hierarchy level from recursive sql query) so i created next projection
#Value
public class ProjectionObject{
MyEntity entity;
int metainfo;
}
#Query(value = "select my_entity.*, 1 as metainfo from my_entities", nativeQuery = true)
List<ProjectionObject> findSome();
But it returns List<List> but i expect List.
As result i what to manipulate with ProjectionObject#entity as with managed (by Entity Manager) ProjectionObject#entity, in other word i want to get managed entity with metainfo once without getting f.e. hierarchy Ids and after get entities
I'm not sure Spring Data Projections supports that.
However, this is a perfect use case for Blaze-Persistence Entity Views.
Blaze-Persistence is a query builder on top of JPA which supports many of the advanced DBMS features on top of the JPA model. I created Entity Views on top of it to allow easy mapping between JPA models and custom interface defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
A projection with Entity Views could look as simple as the following
#EntityView(MyEntity.class)
interface ProjectionObject {
#Mapping("this")
MyEntity getEntity();
#Mapping("1")
int getMetaInfo();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ProjectionObject dto = entityViewManager.find(entityManager, ProjectionObject.class, id);
But the Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
List<ProjectionObject> findAll();
You can also make use of updatable entity views which allows you to eliminate the entity type completely, which reduces the amount of data fetched and flush back only the parts that you actually want to change:
#UpdatableEntityView
#EntityView(MyEntity.class)
interface ProjectionObject {
#IdMapping
Integer getId();
String getName();
void setName(String name);
#Mapping("1")
int getMetaInfo();
}
Now you can fetch that object and then after changing the state flush it back to the database:
ProjectionObject o = repository.findOne(123);
o.setName(o.getName().toUpperCase());
repository.save(o);
And it will only flush back the name as you will see in the SQL.

How can I update a row based on id in hibernate

I am new in hibernate. I am using SesssionFactory to get the session of the transaction and one way which I found after searching is used for setting few fields using set parameter i.e
Query query = getCurrentSession().createSQLQuery(
"UPDATE table_name set field1=:f1 where ID=:id");
query.setParameter("f1", f1);
query.setParameter("id", id);
but I want to update the whole row. I have already set the values in the entity class but is there a way to pass the values of the whole entity class to the database based on the id the id is the primary key for the table which I want to update.
you already have all data present in the hibernate entity object? Then just call the session directly:
getCurrentSession().save(myEntity);
to create a new object, or
getCurrentSession().update(myEntity);
to update an existing row.
If not sure, you can use:
getCurrentSession().saveOrUpdate(myEntity);
Take a look at Session#update (or saveOrUpdate). This will allow you to persist a complete, mapped, object to the database.
To be as OO as you can, you can get entity by session.get(entityClass, id);
And then after modifying object by setters/getters, you can save it back to the DB using update method :session.update(entity);

How to use NumberExpression returned by SQLExpression.datediff() in querydsl

I am doing self-join on a table and using SQLExpression.datediff like so:
QSomeTable tb1 = new QSomeTable("tb1");
QSomeTable tb2 = new QSomeTable("tb2");
NumberExpression<Integer> ne = SQLExpression.datediff(DatePart.hour, tb1.time, tb2.time);
query.from(tb1, tb2).where........list(ne);
This gives the following exception:
org.springframework.dao.InvalidDataAccessApiUsageException: No data type for node: org.hibernate.hql.internal.ast.tree.MethodNode
I tried selecting some other fields in the list clause and it works without problems.
Highly appreciate the help!
SQLExpressions are not supported in Querydsl JPA queries, since they are not supported by JPQL. They are also difficult to emulate since the serialization of these expressions is SQL dialect specific.
So my work around for this problem was to create a view and then use a Spring entity to map the view. Querydsl works seamlessly on top of this entity.
In my case, I did something like this:
View in DB:
create view my_view as select t.id as id, timestampdiff(SECOND,t.time1,t.time2) as timediff from my_table t;
Spring entity
#Entity(name="my_view")
public class MyTableBean{
#Id
#Column(name="id")
Long id;
#Column(name="timediff")
Long timediff;
//Getters and setters
}
Now any query can be written for this view like a normal table
QMyTableBean qt = new QMyTableBean();
// Initilize entity manager and JPQL query
Long res = query.from(qt).where.....list(qt.timediff);
This has the advantage of making the code dialect independent.
Views are supported by all major SQL stores and if a migration happens in the future, impact on application is minimal.

Load Entity from View in JPA/Hibernate

I have an application which uses Spring and Hibernate. In my database there are some views that I need to load in some entities. So I'm trying to execute a native query and load the class withthe data retrieved from the view:
//In my DAO class (#Repository)
public List<MyClass> findMyEntities(){
Query query = em.createNativeQuery("SELECT * FROM V_myView", MyClass.class);
return query.getResultList();
}
and MyClass has the same fields as the column names of the view.
The problem is that Hibernate can't recognize MyClass because it's not an entity (it's not annotated with #Entity)
org.hibernate.MappingException: Unknown entity
If I put MyClass as an entity the system will put try to create/update a table for that entity, because I have configured it :
<property name="hibernate.hbm2ddl.auto" value="update"/>
So I come into these questions:
Can I disable "hibernate.hbm2ddl.auto" just for a single entity?
Is there any way to load the data from a view into a non-entity class?
If not, what would be the best way in my case for loading the data from a view into a class in hibernate?
Thanks
Placed on your class
#Entity
#Immutable
#Subselect(QUERY)
public MyClass {....... }
Hibernate execute the query to retrieve data, but not create the table or view. The downside of this is that it only serves to make readings.
You may use axtavt solution. You may also just execute your query, and transform the List<Object[]> it will return into a List<MyClass> explicitely. Or you may map your view as a read-only entity, which is probably the best solution, because it would allow for associations with other tables, querying through JPQL, Criteria, etc.
In my opinion, hibernate.hbm2ddl.auto should only be used for quick n' dirty prototypes. Use the hibernate tools to generate the SQL file allowing to create the schema, and modify it to remove the creation of the view. Anyway, if it's set to update, shouldn't it skip the table creation since it already exists (as a view)?
You can use AliasToBeanResultTransformer. Since it's a Hibernate-specific feature, you need to access the underlying Hibernate Session:
return em.unwrap(Session.class)
.createSQLQuery("...")
.setResultTransformer(new AliasToBeanResultTransformer(MyClass.class))
.list();

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