Getting a proper stream result with with native annotated queries? - java

I am currently using hibernate 5.0.7 and due to limitations in HQL queries (mainly the lack of a TOP or LIMIT ability) I have come to the point where I am trying to implement a native SQL query.
Due to the way that our repositories are wired up as spring beans from interfaces we are currently using a combination of annotated queries and functional queries to obtain data.
A sample of my query is as follows
#Query(value = "SELECT * FROM some_table where (id = ?1)", nativeQuery = true)
Stream<MyObject> getMyObjectByIds(String userId);
however; my real query is more complex and makes use of GroupBy (something functional queries don't seem to have) and Top (something that direct HQL queries don't seem to have)
All of the tables and items in our database are mapped entities, and all of the data that is currently in the database has been put there by these hibernate entities to begin with. Now, when I run my query I actually get a result back that looks very much like it should be my data I end up getting back
{2, 3, 5, Whatever, null}
and in my data base I have the exact some row values
{2, 3, 5, Whatever, NULL}
however, when I try to access the stream object that I have my function type set as from my native query I end up with the error
org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.Object[]] to type [com.myorg.models.MyObject] for value '{2, 3, 5, Whatever, null}'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.Integer] to type [com.myorg.models.MyObject]
Now from what I've looked up people seem to suggest that there is an SqlResultSetMapping mapping that isn't in place somewhere, and if I look on my entity there is indeed no such mapping
#Entity
#NamedEntityGraph(
name = "MyObject.withChildren",
attributeNodes = #NamedAttributeNode(value = "objectChildren")
)
#Getter
#Setter
#Table(name = "object_table", schema = "my_table")
public class MyObject implements Serializable {
I actually even looked some stuff up and tried to implement a mapping
#SqlResultSetMapping(
name = "ObjectMapping",
entities = {
#EntityResult(
entityClass = MyObject.class,
fields = {
#FieldResult(name = "id", column = "id"),
#FieldResult(name = "childId", column = "child_id"),
#FieldResult(name = "someNumber", column = "some_numbur"),
#FieldResult(name = "someString", column = "some_string"),
#FieldResult(name = "someNullableType", column = "null_type_column")
}
)
}
)
and stuck on my entity class, but I really don't even know how/why/if this is necessary, and worse it didn't even work. Granted that this is a rather obfuscated description of my code/problem is there something simple I'm missing, or does anyone know what I may be doing wrong?
I am thankful for any and all help people are willing to provide!

In the end I've managed to get this working by attaching a #NamedNativeQuery to my entity object itself
#NamedNativeQuery(
name = "MyEntity.complexSqlQuery",
query = "SELECT TOP 10 * FROM my_table WHERE (my_table.id = ?1)",
resultClass = MyEntity.class
and then adding
#Query(nativeQuery = true)
List<MyEntity> complexSqlQuery(String whatever);
to my interface style repository. I don't know if this is ideal as it seems like dumping a crap load of queries onto the entity itself isn't exactly best practice, and I was really hoping for some annotated tag I could just throw on the query already existing in my repository (it seems like the resultClass is exactly what the #Query annotation needs to support this in my case), but it seems no such option exists.
None the less, I did get this solved, just not necessarily in the way I liked.
If anyone has some insight into what I may have missed, I would be very open to hear it, otherwise this is the solution I have come up with until then.

Related

JPA inheritance #EntityGraph include optional associations of subclasses

Given the following domain model, I want to load all Answers including their Values and their respective sub-children and put it in an AnswerDTO to then convert to JSON. I have a working solution but it suffers from the N+1 problem that I want to get rid of by using an ad-hoc #EntityGraph. All associations are configured LAZY.
#Query("SELECT a FROM Answer a")
#EntityGraph(attributePaths = {"value"})
public List<Answer> findAll();
Using an ad-hoc #EntityGraph on the Repository method I can ensure that the values are pre-fetched to prevent N+1 on the Answer->Value association. While my result is fine there is another N+1 problem, because of lazy loading the selected association of the MCValues.
Using this
#EntityGraph(attributePaths = {"value.selected"})
fails, because the selected field is of course only part of some of the Value entities:
Unable to locate Attribute with the the given name [selected] on this ManagedType [x.model.Value];
How can I tell JPA only try fetching the selected association in case the value is a MCValue? I need something like optionalAttributePaths.
You can only use an EntityGraph if the association attribute is part of the superclass and by that also part of all subclasses. Otherwise, the EntityGraph will always fail with the Exception that you currently get.
The best way to avoid your N+1 select issue is to split your query into 2 queries:
The 1st query fetches the MCValue entities using an EntityGraph to fetch the association mapped by the selected attribute. After that query, these entities are then stored in Hibernate's 1st level cache / the persistence context. Hibernate will use them when it processes the result of the 2nd query.
#Query("SELECT m FROM MCValue m") // add WHERE clause as needed ...
#EntityGraph(attributePaths = {"selected"})
public List<MCValue> findAll();
The 2nd query then fetches the Answer entity and uses an EntityGraph to also fetch the associated Value entities. For each Value entity, Hibernate will instantiate the specific subclass and check if the 1st level cache already contains an object for that class and primary key combination. If that's the case, Hibernate uses the object from the 1st level cache instead of the data returned by the query.
#Query("SELECT a FROM Answer a")
#EntityGraph(attributePaths = {"value"})
public List<Answer> findAll();
Because we already fetched all MCValue entities with the associated selected entities, we now get Answer entities with an initialized value association. And if the association contains an MCValue entity, its selected association will also be initialized.
I don't know what Spring-Data is doing there, but to do that, you usually have to use the TREAT operator to be able to access the sub-association but the implementation for that Operator is quite buggy.
Hibernate supports implicit subtype property access which is what you would need here, but apparently Spring-Data can't handle this properly. I can recommend that you take a look at Blaze-Persistence Entity-Views, a library that works on top of JPA which allows you map arbitrary structures against your entity model. You can map your DTO model in a type safe way, also the inheritance structure. Entity views for your use case could look like this
#EntityView(Answer.class)
interface AnswerDTO {
#IdMapping
Long getId();
ValueDTO getValue();
}
#EntityView(Value.class)
#EntityViewInheritance
interface ValueDTO {
#IdMapping
Long getId();
}
#EntityView(TextValue.class)
interface TextValueDTO extends ValueDTO {
String getText();
}
#EntityView(RatingValue.class)
interface RatingValueDTO extends ValueDTO {
int getRating();
}
#EntityView(MCValue.class)
interface TextValueDTO extends ValueDTO {
#Mapping("selected.id")
Set<Long> getOption();
}
With the spring data integration provided by Blaze-Persistence you can define a repository like this and directly use the result
#Transactional(readOnly = true)
interface AnswerRepository extends Repository<Answer, Long> {
List<AnswerDTO> findAll();
}
It will generate a HQL query that selects just what you mapped in the AnswerDTO which is something like the following.
SELECT
a.id,
v.id,
TYPE(v),
CASE WHEN TYPE(v) = TextValue THEN v.text END,
CASE WHEN TYPE(v) = RatingValue THEN v.rating END,
CASE WHEN TYPE(v) = MCValue THEN s.id END
FROM Answer a
LEFT JOIN a.value v
LEFT JOIN v.selected s
My latest project used GraphQL (a first for me) and we had a big issue with N+1 queries and trying to optimize the queries to only join for tables when they are required. I have found Cosium
/
spring-data-jpa-entity-graph irreplaceable. It extends JpaRepository and adds methods to pass in an entity graph to the query. You can then build dynamic entity graphs at runtime to add in left joins for only the data you need.
Our data flow looks something like this:
Receive GraphQL request
Parse GraphQL request and convert to list of entity graph nodes in the query
Create entity graph from the discovered nodes and pass into the repository for execution
To solve the problem of not including invalid nodes into the entity graph (for example __typename from graphql), I created a utility class which handles the entity graph generation. The calling class passes in the class name it is generating the graph for, which then validates each node in the graph against the metamodel maintained by the ORM. If the node is not in the model, it removes it from the list of graph nodes. (This check needs to be recursive and check each child as well)
Before finding this I had tried projections and every other alternative recommended in the Spring JPA / Hibernate docs, but nothing seemed to solve the problem elegantly or at least with a ton of extra code
Edited after your comment:
My apologize, I haven't undersood you issue in the first round, your issue occurs on startup of spring-data, not only when you try to call the findAll().
So, you can now navigate the full example can be pull from my github:
https://github.com/bdzzaid/stackoverflow-java/blob/master/jpa-hibernate/
You can easlily reproduce and fix your issue inside this project.
Effectivly, Spring data and hibernate are not capable to determinate the "selected" graph by default and you need to specify the way to collect the selected option.
So first, you have to declare the NamedEntityGraphs of the class Answer
As you can see, there is two NamedEntityGraph for the attribute value of the class Answer
The first for all Value without specific relationship to load
The second for the specific Multichoice value. If you remove this one, you reproduce the exception.
Second, you need to be in a transactional context answerRepository.findAll() if you want to fetch data in type LAZY
#Entity
#Table(name = "answer")
#NamedEntityGraphs({
#NamedEntityGraph(
name = "graph.Answer",
attributeNodes = #NamedAttributeNode(value = "value")
),
#NamedEntityGraph(
name = "graph.AnswerMultichoice",
attributeNodes = #NamedAttributeNode(value = "value"),
subgraphs = {
#NamedSubgraph(
name = "graph.AnswerMultichoice.selected",
attributeNodes = {
#NamedAttributeNode("selected")
}
)
}
)
}
)
public class Answer
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false)
private int id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "value_id", referencedColumnName = "id")
private Value value;
// ..
}

Hibernate is making extra SQL statement with #ManyToOne and #Lazy fetching object

I would like someone to explain me why Hibernate is making one extra SQL statement in my straight forward case. Basically i have this object:
#Entity
class ConfigurationTechLog (
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long?,
val configurationId: Long,
val type: String,
val value: String?
) {
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "configurationId", insertable = false, updatable = false)
val configuration: Configuration? = null
}
So as you can see, nothing special there. And when i execute this query :
#Query(value = "SELECT c FROM ConfigurationTechLog c where c.id = 10")
fun findById10() : Set<ConfigurationTechLog>
In my console i see this:
Hibernate:
/* SELECT
c
FROM
ConfigurationTechLog c
where
c.id = 10 */ select
configurat0_.id as id1_2_,
configurat0_.configuration_id as configur2_2_,
configurat0_.type as type3_2_,
configurat0_.value as value4_2_
from
configuration_tech_log configurat0_
where
configurat0_.id=10
Hibernate:
select
configurat0_.id as id1_0_0_,
configurat0_.branch_code as branch_c2_0_0_,
configurat0_.country as country3_0_0_,
configurat0_.merchant_name as merchant4_0_0_,
configurat0_.merchant_number as merchant5_0_0_,
configurat0_.org as org6_0_0_,
configurat0_.outlet_id as outlet_i7_0_0_,
configurat0_.platform_merchant_account_name as platform8_0_0_,
configurat0_.store_type as store_ty9_0_0_,
configurat0_.terminal_count as termina10_0_0_
from
configuration configurat0_
where
configurat0_.id=?
Can someone please explain me, what is happening here ? From where this second query is coming from ?
I assume you are using Kotlin data class. The kotlin data class would generate toString, hashCode and equals methods utilizing all the member fields. So if you are using the returned values in your code in a way that results in calling of any of these method may cause this issue.
BTW, using Kotlin data claases is against the basic requirements for JPA Entity as data classes are final classes having final members.
In order to make an association lazy, Hibernate has to create a proxy instance instead of using the real object, i.e. it needs to create an instance of dynamically generated subclass of the association class.
Since in Kotlin all classes are final by default, Hibernate cannot subclass it so it has to create the real object and initialize the association right away. In order to verify this, try declaring the Configuration class as open.
To solve this without the need to explicitly declare all entities open, it is easier to do it via the kotlin-allopen compiler plugin.
This Link can be useful for understand what kind (common) problem is that N + 1 Problem
Let me give you an example:
I have three Courses and each of them have Students related.
I would like to perform a "SELECT * FROM Courses". This is the first query that i want (+ 1) but Hibernate in background, in order to get details about Students for each Course that select * given to us, will execute three more queries, one for each course (N, there are three Course coming from select *). In the end i will see 4 queries into Hibernate Logs
Considering the example before, probably this is what happen in your case: You execute the first query that you want, getting Configuration Id = 10 but after, Hibernate, will take the entity related to this Configuration, then a new query is executed to get this related entity.
This problem should be related in specific to Relationships (of course) and LAZY Fetch. This is not a problem that you have caused but is an Hibernate Performance Issue with LAZY Fetch, consider it a sort of bug or a default behaviour
To solve this kind of problem, i don't know if will be in your case but ... i know three ways:
EAGER Fetch Type (but not the most good option)
Query with JOIN FETCH between Courses and Students
Creating EntityGraph Object that rappresent the Course and SubGraph that rappresent Students and is added to EntityGraph
Looking at your question, it seems like an expected behavior.
Since you've set up configuration to fetch lazily with #ManyToOne(fetch = FetchType.LAZY), the first sql just queries the other variables. When you try to access the configuration object, hibernate queries the db again. That's what lazy fetching is. If you'd like Hibernate to use joins and fetch all values at once, try setting #ManyToOne(fetch = FetchType.EAGER).

Java loading entities partially with JPA 2.1 and hibernate on #ManyToOne

I have an entity with a lot of attributes, relationships inside. In a few cases I just need 2 simple attributes, not the rest. I tried using entity graphs, but I always get the complete entries, with all attributs, relationships...
The entity graph in my EECase-entity:
#NamedQueries({
#NamedQuery(name = EECase.QUERY_ALLCASES, query = "SELECT DISTINCT c FROM EECase c")
})
#NamedEntityGraph(name = "Case.forDropdown", attributeNodes = {
#NamedAttributeNode("caseNumber"),
#NamedAttributeNode("firstNames"),
#NamedAttributeNode("lastName")
})
In my bean I try to get the filtered cases with:
public List<EECase> getCasesForDropdown() {
TypedQuery<EECase> query = getManager().createNamedQuery(EECase.QUERY_ALLCASES, EECase.class);
EntityGraph<EECase> graph = (EntityGraph<EECase>) getManager().getEntityGraph("Case.forDropdown");
query.setHint("javax.persistence.fetchgraph", graph);
List<EECase> queryEntity = (List<EECase>) query.getResultList();
return queryEntity;
}
It seems the setHint is getting ignored?
Even if you define a hint, its still an optional thing.
I would suggest a small alternative in the form of a result class in the select being a projection (advised option if you do not plan to update the entity afterwards):
#NamedQueries({
#NamedQuery(name = EECase.QUERY_ALLCASES
, query = "SELECT new com.domain.EECase(c.caseNumber, c.firstName, c.lastName)
FROM EECase c")
})
Keep in mind to place a proper constructor to accept the columns of the projection in a given order.
You can also use a separate POJO to map the results of that query. Not necessarily the entity class itself.
Also keep in mind that you wont be able to select entire dependent entity.. only plain attributes (i assumed that is the case).

Hibernate, How can I detach and/or get data only on demand?

I have an entity setup like this:
#Entity
#Table(name = "APP", uniqueConstraints = #UniqueConstraint(columnNames = "APP_KEY"))
public class Application implements java.io.Serializable {
...
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "application_Id", referencedColumnName = "application_Id")
private Set<Document> documents = new HashSet<Document>(0);
}
Now, in some situations I don't want the list of documents to be returned. However when I serialize this object the "getDocuments()" method will be called.
There will not be an active transaction at this time, so I don't want one of those "no session" errors. I just want to ignore it and have the getDocuments() method return empty, not throw an exception and not try to fetch more data.
Take a look at the FetchGroup capability ref:
Take a look at the jpql fetch join's...
Select e from Employee e join fetch e.phones p where p.areaCode = '613'
This was covered for a similar question here: How to properly express JPQL "join fetch" with "where" clause as JPA 2 CriteriaQuery?
I do not know your serialization setup, however:
If you are using Jackson, you can use this : https://github.com/FasterXML/jackson-datatype-hibernate which works quite nicely.
If you have a manual serialization setup, you should probably not be calling methods directly, without prior knowledge of their initialization status. What you could do is use something like Hibernate.isInitialized(object.Method)
https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/Hibernate.html#isInitialized-java.lang.Object-

Simple jpa question

I have to go through some code of a project to implement some missiong functionality.
It uses jpa.In some popj classes i have found
#Entity
#Table(name="TYPE")
#NamedQueries( {
#NamedQuery(name = "getTypes", query = "SELECT dct FROM Type dct")
})
I know that i can used to get all records by using this query.Does this query return all records in type table?
This query will return all the Type entities including subtypes, if any. And since I can't say if this there are any subtypes, I can't say if this query will be restricted to the TYPE table.
Yes, it does. It generates an SQL query that looks roughly like this:
SELECT [column list here] FROM type

Categories