ResultSet in Spring Data JPA - java

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.

Related

How to handle 2 OUT user-defined custom data types from Java Spring Data JPA #NamedStoredProcedureQuery defined on #Entity class?

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?

How to map One to Many / One to One to a POJO in Spring Data R2DBC

I am trying to replicate some functionality that I was using in Spring Data JPA in the new reactive Data r2dbc. I am aware that r2dbc is not a full-fledged ORM but wanted to understand as what could be the best way to replicate the below scenario in r2dbc:
public class Doctor extends BaseModel {
//other fields and id
#NotNull
#Enumerated(EnumType.STRING)
#ElementCollection(fetch = FetchType.LAZY, targetClass = Language.class)
#CollectionTable(name = "doctor_language",
joinColumns = #JoinColumn(name = "doctor_id"))
#Column(name = "language")
private List<Language> languages = new ArrayList<>();
#OneToMany(fetch = FetchType.LAZY, targetEntity = DoctorHealthProvider.class, mappedBy =
"doctor")
private List<DoctorHealthProvider> providers = new ArrayList<>();
// other fields
}
A simple findById call on DoctorRepository (which extends JpaRepository) will give me doctor object with list of languages from doctor_language table and list of health providers from health_provider table if I use Spring Data JPA
I was reading about projections but couldn't seem to figure out the best way to implement the above in Reactive Spring. Any help/guidelines/direction is appreciated.
Thanks
You may have a look at https://github.com/lecousin/lc-spring-data-r2dbc but looks like not yet fully ready to be used. But if your needs are not too complex it may do the job.
For example you can declare links like that:
#Table
public class TableWithForeignKey {
...
#ForeignKey(optional = false)
private LinkedTable myLink;
...
}
#Table
public class LinkedTable {
...
#ForeignTable(joinkey = "myLink")
private List<TableWithForeignKey> links;
...
}
When you want the links to be mapped, you can use either lazy loading or select with join.
If you use the methods findBy... (findById, findAll...) of the Spring repository, only the table of the repository will be loaded. In this case, you can use lazy loading. For that you need to declare a methods, with default body, and your method will be automatically implemented:
public Flux<TableWithForeignKey> lazyGetLinks() {
return null; // will be implemented
}
Another way is to make joins directly in the request. There is currently no support to automatically do joins in a repository (like #EntitiGraph in JPA), but you can implement your methods like this:
public interface MyRepository extends LcR2dbcRepository<LinkedTable, Long> {
default Flux<LinkedTable> findAllAndJoin() {
SelectQuery.from(LinkedTable.class, "root") // SELECT FROM LinkedTable AS root
.join("root", "links", "link") // JOIN root.links AS link
.execute(getLcClient()); // execute the select and map entities
}
}
The result will be all LinkedTable instances, with the list of links loaded together from the database.
Alternatively try Micronaut framework, it adopts the good parts from all existing frameworks and provides a lot of missing features in spring/SpringBoot.
For example, Micronaut Data R2dbc, it provides a collection of annotations(including one to many, many to many, embedded) to define relations like JPA, more simply you can use JPA annotations and JPA like Specifciation to customize query via type safe Criteria APIs.
Create a web application via https://micronaut.io/launch/, add R2dbc and Database drivers to dependencies, you can bring your Spring experience to Micronaut without extra effort.
My Micronaut Data R2dbc example: https://github.com/hantsy/micronaut-sandbox/tree/master/data-r2dbc

Spring, hibernate type mapping based on different database

I have something like this for defining the hibernate type mapping in the entity class:
#Entity
#Table(name = "TEST_TABLE")
public class Test {
#Type( type = "jsonb" )
#Column(name = "CONTENT_FILES")
private List<ContentFile> contentFiles;
}
which map an entity field to a custom defined hibernate type jsonb for supporting PostgreSQL DB.
I would like to change the mapping to another hibernate custom type json for supporting MSSQL DB.
Can I support both mappings in the same entity class?
I tried to use the #Profile annotation but it doesn't work.
#Profile("pgsql")
#Type( type = "jsonb" )
#Profile("mssql")
#Type( type = "json" )
#Column(name = "CONTENT_FILES")
private List<ContentFile> contentFiles;
json and jsonb both data types are almost identical according to the PostgreSQL documentation.Therefor you don't have to maintain two different data types to keep json in MSSQL and in PostgreSQL .
Please refer to below link of
PostgreSQL documentation.

How to define Stored Procedure outside JPA Repository

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.

Getting a proper stream result with with native annotated queries?

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.

Categories