I have some stored procedures which select data from multiple entities, so where should i define them as they are not getting there data from a single repository?
i have defined stored procedures specific which are strictly getting data from single table in there entity classes like this
#Entity
#Table(name = "accounts", schema = "ma_db")
#NamedStoredProcedureQueries({
#NamedStoredProcedureQuery(name="getAccountsList", procedureName = "GET_ACCOUNT", parameters = {
#StoredProcedureParameter(mode = ParameterMode.IN, name = "UserId", type = String.class)
} )
It's really impossible to tell without more information about you application and the SP in question.
But here are some guidelines:
Think about what the SP is doing. What is the main domain concept it is concerned with?
This doesn't have to be an entity, maybe you don't need such an entity in your java code, maybe you need it and haven't realized it yet.
One typical example where I have seen this situation is with reports or exports. Those are domain objects as well, although they often don't match to a JPA entity.
If there really isn't a matching entity to associate the SP with, maybe the right thing to do is to just create a simple class that executes the SP using a JdbcTemplate.
Related
UPDATE:
I have tried implementing the SQLData interface for my Financial.class and PersonalInfo.class Java POJOs defined on my #NamedStoredProcedureQuery for the type. I also implemented the required getSQLTypeName, readSQL, and writeSQL methods per an Oracle doc:
https://docs.oracle.com/cd/A97335_02/apps.102/a83724/samapp6.htm
I was hoping this would work, but it looks like its still giving me the same Type cannot be null exception. Does it matter that these personal_information_t and financial_t Objects defined in my Oracle DB are inheriting from a superclass, called base_t ?
Hi guys, I'm simply trying to use the #NamedStoredProcedureQuery directly on my #Entity class to call a Stored Procedure that is in my Oracle Database (that is in a separate schema, "JJR"). I can indeed correctly connect to this database programmatically from my Java Spring Boot application, and I can run JPA queries successfully like .findAll() so I know its not a connection issue but I believe something to do with my #NamedStoredProcedure declaration. All the tutorials on Google for Spring JPA Stored Procedure queries are using standard data types like Long or String, and only returning one OUT parameter.
However, I'm returning two OUT parameters and to make it more complicated, they are user-defined types (defined in the Oracle Database), so I'm trying to figure out how to handle this as my current attempt is returning back this exception:
org.springframework.dao.InvalidDataAccessApiUsageException: Type cannot be null; nested exception is java.lang.IllegalArgumentException:
My Stored Procedure structure (It is inside a Package in my oracle db of pkg_employee_data in schema/user JJR). The user-defined types (personal_information_t and financial_t both have multiple fields in them, i.e. personal_information_t has an userid, firstname, lastname, and financial_t has salary, networth etc..:
PROCEDURE get_employee_data (
empid IN emp.emp_id%TYPE, // NUMBER(38,0)
persinfo OUT personal_information_t, // user-defined type
financ OUT financial_t / user-defined type
);
And how I'm defining the #NamedStoredProcedureQuery
#Entity
#NamedStoredProcedureQuery(name = "Employee.getEmployeeData",
procedureName = "pkg_employee_data.get_employee_data", parameters = {
#StoredProcedureParameter(mode = ParameterMode.IN, name = "empid", type = Long.class),
#StoredProcedureParameter(mode = ParameterMode.OUT, name = "persinfo", type = PersonalInfo.class),
#StoredProcedureParameter(mode = ParameterMode.OUT, name = "financ", type = Financial.class)})
public class Employee {
// Should I list the columns I want mapped here in the Entity
// from the "persinfo" and "financ" user-defined types, that
// should have a bunch fields/data inside them?
#Id
private long userId;
private String firstName;
private Double salary;
}
And the #Repository declaration, where I map this method:
#Repository
public interface EmployeeRepository extends JpaRepository<Employee,Long> {
#Procedure(name = "Employee.getEmployeeData")
Map<String, Object> getEmployee(#Param("empid") long empid);
}
Are my type = "*.class" declarations even correct on the #NamedStoredProcedureQuery annotation? seems like my Types ares getting read a null. I created a PersonalInfo.java and Financial.java #Entities in my code as I hoped it would map from the Oracle user-defined types but it doesn't seem like that's working.
On all my #Entities, I have to declare some meta-information like #Table(schema = "JJR", name = "MY_TABLE_NAME") because as I stated above, I'm logging into a specific user/schema in my Oracle DB. Do I need to put this on the #NamedStoredProcedureQuery too? I'm not mapping to a table here technically though (I just need the data from the two OUT parameters).
I even tried using java.sql.Struct.class as the Type in the #NamedStoredProcedureQuery per Google, but nothing seems like it's working.
What actually columns/fields do I need defined in the Employee Entity, which has the #NamedStoredProcedureQuery annotation on it? I'm not really mapping to a Table, like normally we use #Entity. This stored procedure is just returning those two OUT parameters (personal_information_t and financial_t and I need the data from it). Should the fields in Employee class be simply the fields that are in the Oracle user-defined types that I need?
Suppose an entity model where an Employee has a Supervisor who has an id. Using hibernate-jpamodelgen to generate the meta model for the entities, how can I query a nested field?
For instance, "get all employees whose supervisor has id 4", using JpaSpecificationExecutor:
Page<Employee> getEmployeesBySupervisorId(int id) {
return findAll((root, query, criteriaBuilder) -> {
return criteriaBuilder.equal(root.get(Employee_.supervisor.id), id);
});
}
Note that Employee_ is the model meta class for Employee (and was generated by Hibernate).
This code will produce an error because the id symbol cannot be found on type SingularAttribute<Employee, Supervisor>. I get that, but it seems like these should somehow be chainable. I can't find great examples of how to do this cleanly.
In order to navigate to related entities, you must use From#join() join method, which works well with MetaModel:
CriteriaQuery<Employee> cq = criteriaBuilder.createQuery(Employee.class);
Root<Employee> from = cq.from(Employee.class);
Predicate p = criteriaBuilder.equal(from.join(Employee_.supervisor).get(Supervisor_.id), id);
See also
Oracle's Java EE Tutorial - Using the Criteria API and Metamodel API to Create Basic Typesafe Queries
Yes, I also stumbled upon this problem that the Metamodel classes are not offering deeper visibility to relationships > 1.
While accessing A.b is possible, A.b.c is not.
But there is another possibility besides Joins:
Just concatenate by using several getter(). For this you will need a root element (= CriteriaQuery & CriteriaBuilder).
return criteriaBuilder.equal(root.get(Employee_.supervisor).get(Supervisor_.id), id);
While this still ensures type safety, the whole path should be correct as it is not validated until runtime.
Also for sorting a resultset using the Metamodel there is a similar solution. Say you want to sort by the Supervisor's id:
Use JpaSort and JpaSort.Path
JpaSort.of(JpaSort.Direction.ASC, JpaSort.path(Employee_.supervisor).dot(Supervisor_.id));
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).
In JDBC after executing the store procedure we will be getting ResultSet, so i was wondering how can i achieve the same using Spring Data JPA?
I am having a Entity something like below
#Entity
#Table(name = "MyTable")
#NamedStoredProcedureQuery(name = "MyTable.in_and_out_test", procedureName = "in_and_out_test", parameters = {
#StoredProcedureParameter(mode = ParameterMode.IN, name = "XML", type = String.class)})
public class MyTable implements java.io.Serializable {
....
}
Main benefit of using ORM is to avoid mapping between your objects and rows in tables in database (at least in most cases that work out of box with properly annotated entity classes) , so I if I understand correctly what are you asking, than I have to say that I do not see any point in your question.
I suggest to explore a little bit more Spring Data JPA which use Hibernate as implementation by default and to learn how to use benefits of that Spring project.
I have a persistent class NewsClass with persistent field newsSource.
// PERSISTENT
class NewsClass {
// Persistent
String newsSource;
// Other persistent fields
}
Now to query this entity
Query q = pm.newQuery(NewsClass.class);
q.setFilter("newsSource=='http://somerandomurl'");
List<NewsClass> result = (List<NewsClass>) q.execute();
It turns out that JDO doesn't look for the newsSource field but rather tries an instantiation like new sSource(). I also tried things like q.setFileter("\"newsSource\"=='http://reandomurl'"); as a workaround but didn't work either.
There's about 1GB of data already (on the AppEngine datastore which uses a soft schema) so renaming the field doesn't really look like a good idea.
Please how do I make this query work?
EDIT
Here's what I got in my logger.
CreatorExpression defined with class of sSourceId yet this class is not found