I use oracle stored procedures with spring-data-jpa. In the most cases it is pretty well, when it is a function or output parameter is first in the param list. But I have some stored procedures with ouput param is the last in the param list:
procedure get_data (some_val in varchar2 cur out sys_refcursor);
or returns more than one output refcursors like
procedure get_my_data (cur1 out sys_refcursor, cur2 out sys_refcursor, some_val in varchar2);
Is it possible with any way to use it with JpaRepository?
Finally, I've found answer by myself :)
My situation is a good point for using spring-data's Custom Implementation
You should:
Create Interface, named YourRepository*Custom* with your new
method (for example EmployeeRepositoryCustom)
Create an imiplementation for this interface with name
YourRepository*Impl* (for example EmployeeRepositoryImpl)
Inside method implementation you can use
SimpleJdbcCall for calling Oracle stored procedure, for example
...
PROFIT!!!
Note: naming rules are important if you want to use default configs
well,
if you want a localized call to a StoredProc from a service or utility class, then you can use Spring Jdbc for StoredProc. See below for implementation
public class StoredProcSampleTest extends StoredProcedure {
private static final String SPROC_NAME = "HH_EXTRACT.SAMPLE_TEST";
public StoredProcSampleTest(DataSource dataSource) {
super(dataSource, SPROC_NAME);
declareParameter(new SqlParameter("v_in_msg", Types.VARCHAR));
declareParameter(new SqlOutParameter("v_out_msg", Types.VARCHAR));
compile();
}
public String execute() {
HashMap<String, Object> hmap = new HashMap<String, Object>();
hmap.put("v_in_msg", "Suresh");
hmap.put("v_out_msg", "");
Map<String, Object> results = execute(hmap);
String outRes = (String) results.get("v_out_msg");
return outRes;
}
}
now in your utility class or Service class, do this
protected StoredProcSampleTest storedProcSampleTest;
#Autowired
public void setDataSource(final DataSource dataSource) {
this.storedProcSampleTest = new StoredProcSampleTest(dataSource);
}
:
public String callStoredProcSampleTest(){
return storedProcSampleTest.execute();
}
Related
I am using org.springframework.data.mongodb.repository.MongoRepository. I have written some custom method like below,
public interface DocRepository extends MongoRepository<Doc, String> {
Doc findByDocIdAndAssignmentId(final String docId, final String assignemtId);
}
How can I write a custom method which update all entries when meeting a criteria.
For example set document tilte field to "abc" if assignment id is "xyz"?
1) You need to create inteface e.g CustomDocRepository and add this interfaces as Base for your DocRepository:
public interface DocRepository extends MongoRepository<Doc, String>, CustomDocRepository {
void updateDocumentTitle(String id, String title);
}
2) You need to add implementation for the DocRepository:
#Repository
public class CustomDocRepositoryImpl implements DocRepository {
#Autowired
private MongoTemplate mongoTemplate;
#Override
public void updateDocumentTitle(String id, String title) {
Query query = new Query().addCriteria(where("_id").is(id));
Update update = new Update();
update.set("title", title);
mongoTemplate.update(Doc.class).matching(query).apply(update).first();
}
}
That is all you need to do
Provided you have an autowired attribute mongoTemplate in your service class. Add the below code to update the document.
Query query = new Query();
query.addCriteria(Criteria.where("assignmentId").is("xyz"))
Update update = new Update();
update.set("title", "abc");
mongoTemplate.updateFirst(query, update, Doc.class);
You dont need to have findByDocIdAndAssignmentId for the update purpose.
I found com.mongodb.MongoClient to achieve the above
I hope the title makes sense.
Let's say I have a stored procedure (in Microsoft SQL Server) which generates a select statement based on some parameters and then executes that select statement on a table. Let's say the table is Users and the select statement returns the first user in the table. The user is has an ID, a fname, and an lname.
How can I store the data that is generated by the select statement?
In eclipse, I want to use Spring and JdbcTemplate, and I'm thinking about using a callable statement. Any ideas?
From the Spring documentation:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jdbc.html
private class GetSysdateProcedure extends StoredProcedure {
private static final String SQL = "sysdate";
public GetSysdateProcedure(DataSource dataSource) {
setDataSource(dataSource);
setFunction(true);
setSql(SQL);
declareParameter(new SqlOutParameter("date", Types.DATE));
compile();
}
public Date execute() {
// the 'sysdate' sproc has no input parameters, so an empty Map is supplied...
Map<String, Object> results = execute(new HashMap<String, Object>());
Date sysdate = (Date) results.get("date");
return sysdate;
}
}
I am struggling with the below code to make it work, searching documentation and forums and stucked.
Finally I decided to ask you for help.
What I have is package with TYPES, FUNCTION declarations and FUNCTION BODY declaration.
In future I would like to use SYNONYM to MYPACKAGE (This is only mock - I will not have package and types declarations in my database, but use dblink to external database and Java code to run procedures / functions, but now I don't have this dblink accessible) and MYPACKAGE will be something accessible through dblink:
create public synonym dblink_MYPACKAGE for SOME_SCHEMA.MYPACKAGE#dblink_externalDB;
and I will be using dblink_MYPACKAGE instead of MYPACKAGE in Java Code.
(but this doesn't matter does it?) The external database is not ours, so we CAN'T change anything there...
public class TestClassSpringBased {
private DataSource dataSource;
private SimpleJdbcCall jdbcCall;
#Override
public void testMe(Integer id) {
int iid = 1;
SqlParameterSource in = new MapSqlParameterSource().addValue("IN_1", iid);
Map<String, Object> out = jdbcCall.execute(in);
}
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.jdbcCall = new SimpleJdbcCall(dataSource)
.withCatalogName("MYPACKAGE")
.withProcedureName("MYFUNCTION")
.withReturnValue()
.useInParameterNames("IN_1")
.declareParameters(
new SqlInOutParameter("IN_1", OracleTypes.NUMBER),
new SqlInOutParameter("OUT_1", OracleTypes.STRUCT, "MYPACKAGE.CUSTOMELEMENTSTYPE",
new SqlReturnType() {
public Object getTypeValue(CallableStatement cs, int colIndx, int sqlType,
String typeName) throws SQLException {
return null; //just let it work, the I will think what to write here
}
}));
}
}
create or replace
PACKAGE MYPACKAGE IS
TYPE CUSTOMELEMENTSTYPE_R IS RECORD (
C1 VARCHAR2(60),
C2 VARCHAR2(30)
);
TYPE CUSTOMELEMENTSTYPE IS TABLE OF CUSTOMELEMENTSTYPE_R
INDEX BY PLS_INTEGER;
FUNCTION MYFUNCTION(
IN_1 IN INTEGER, OUT_1 OUT CUSTOMELEMENTSTYPE )
RETURN VARCHAR2;
END;
create or replace
PACKAGE BODY MYPACKAGE IS
FUNCTION MYFUNCTION(
IN_1 IN INTEGER, OUT_1 OUT CUSTOMELEMENTSTYPE )
RETURN VARCHAR2 IS
BEGIN
SELECT * BULK COLLECT INTO OUT_1
FROM SOME_TABLE;
RETURN 'return param';
END MYFUNCTION;
END MYPACKAGE ;
The ERROR is:
org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback; uncategorized SQLException for SQL [{? = call MYPACKAGE.MYFUNCTION(?, ?)}]; SQL state [99999]; error code [17074]; invalid name pattern: MYPACKAGE.CUSTOMELEMENTSTYPE; nested exception is java.sql.SQLException: invalid name pattern: MYPACKAGE.CUSTOMELEMENTSTYPE
The problem is only with OUT parameter, the same code works, when I dont pass OUT parameter and run it against another version of MYFUNCTION, that has not OUT parameter.
I tried also with OracleTypes.ARRAY (invalid name pattern) and OracleTypes.OTHER (Caused by: java.sql.SQLException: wrong column type: 1111)
It seems that You use incorrect method call:
Your code:
.withProcedureName("MYFUNCTION")[..]
should be replaced by
.withFunctionName[...]
here is some simple examle of whole function call:
JdbcTemplate jdbc = new JdbcTemplate(txManager.getDataSource());
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbc)
.withCatalogName("p_adm_www")
.withFunctionName("fn_usr_get_login_sequence")
.declareParameters(new SqlOutParameter("RETURN", OracleTypes.NUMBER))
.withoutProcedureColumnMetaDataAccess();
jdbcCall.setAccessCallParameterMetaData(false);
BigDecimal returnId = jdbcCall.executeFunction(BigDecimal.class, null);
return returnId.longValue();
I am studying how to execute query on a database using JDBC in Spring Framework.
I am following this tutorial: http://www.tutorialspoint.com/spring/spring_jdbc_example.htm
In this tutorial I define a StudentDAO interface which only define the CRUD method that I want.
Then is defined the Student class that is the entity that I want to persist on the Student database table.
Then is defined the StudentMapper class that is a specific implementation of RowMapper interface that, in this case, is used to map a specific record in the ResultSet (returned by a query) to a Student object.
Then I have the StudentJDBCTemplate that rappresent the implementation of my StudentDAO interface, in this class I implement the CRUD method that was defined in the interface.
Ok, and now I have a doubt about how the StudentMapper class work: in this StudentJDBCTemplate class there is defined the method that return the list of all record that are in the Student database table, this one:
public List<Student> listStudents() {
String SQL = "select * from Student";
List <Student> students = jdbcTemplateObject.query(SQL,
new StudentMapper());
return students;
}
How you can see, this method return a List of Student object and work in the following way:
the first thing that it do is to define the query that return all record in the Student database table in the SQL String.
Then this query is executed by the query method call on the jdbcTemplateObject object (that is an istance of JdbcTemplate Spring class**
This method take two parameter: the SQL String (that contains the SQL query that must be executed) and a new StudentMapper object that take the ResultSet object returned by the query and map it's record on a new Student object
Reading here: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html sayas that: Execute a query given static SQL, mapping each row to a Java object via a RowMapper.
My doubt is related to the fact that my StudentMapper map a ResultSet record on a Student object using the mapRow() method, this is the code:
package com.tutorialspoint;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class StudentMapper implements RowMapper<Student> {
public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
Student student = new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
student.setAge(rs.getInt("age"));
return student;
}
}
So, who call this mapRow method? is it called automatically by the Spring Framework? (because in this example is never called manually...)
Tnx
Andrea
Then this query is executed by the query method call on the jdbcTemplateObject object (that is an istance of JdbcTemplate Spring class**
When you pass an instance of your RowMapper to the JdbcTemplate method
List <Student> students = jdbcTemplateObject.query(SQL, new StudentMapper());
The JdbcTemplate depending on which method you called, will internally use the mapper with the result set it gets from the JDBC Connection to create an object of your requested type. For example, since you called JdbcTemplate#query(String, RowMapper), the method will use your String SQL to query the database and will loop through each "row" in the ResultSet kind of like this:
ResultSet rs = ... // execute query
List<Student> students = ...// some list
int rowNum = 0;
while(rs.next()) {
Student student = rowMapper.mapRow(rs, rowNum);
students.add(student);
rowNum++;
}
return students;
So, Spring's JdbcTemplate method will use the RowMapper you provide and call its mapRow method to create the expected return object.
You might like to look at Martin Fowler's Data Mapper in conjunction with Table Data Gateway for an idea of how these things are distributed and provide low coupling.
Here is the typical pattern I use with BeanPropertyRowMapper. It saves a lot of coding. Your query needs to alias each column to match the property name in the class. In this case species_name as species and the other column names happen to match already.
public class Animal {
String species;
String phylum;
String family;
...getters and setters omitted
}
#Repository
public class AnimalRepository {
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
#Autowired
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public List<Animal> getAnimalsByPhylum(String phylum) {
String sql = " SELECT species_name as species, phylum, family FROM animals"
+" WHERE phylum = :phylum";
Map<String, Object> namedParameters = new HashMap<String, Object>();
namedParameters.put("phylum", phylum);
SqlParameterSource params = new MapSqlParameterSource(namedParameters);
List<Animal> records = namedParameterJdbcTemplate.query(sql,
params, BeanPropertyRowMapper.newInstance(Animal.class));
return records;
}
}
An alternative is to use a RowMapper (this example just uses an anonymous class) when you need more customization per row:
List<Animal> records = namedParameterJdbcTemplate.query(sql,
params, new RowMapper<Animal>(){
public Animal mapRow(ResultSet rs, int i) throws SQLException {
Animal animal = new Animal();
animal.setSpecies(rs.getString("species_name"));
if (some condition) {
animal.setPhylum(rs.getString("phylum"));
} else {
animal.setPhylum(rs.getString("phylum")+someThing());
}
animal.setFamily(rs.getString("family"));
return animal;
}
});
Using RowMapper in Spring
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class RowsMap implements RowMapper<EmpPojo>{
#Override
public EmpPojo mapRow(ResultSet rs, int counts) throws SQLException {
EmpPojo em=new EmpPojo();
em.setEid(rs.getInt(1));
em.setEname(rs.getString(2));
em.setEsal(rs.getDouble(3));
return em;
}
}
Finally in Main class
List<EmpPojo> lm=jt.query("select * from emps", new RowsMap());
for(EmpPojo e:lm)
{
System.out.println(e.getEid()+" "+e.getEname()+" "+e.getEsal());
}
So, who call this mapRow method? is it called automatically by the
Spring Framework? (because in this example is never called
manually...)
This is automatically called by spring framework.
All You need is to specify
Connection parameters,
SQL statement
Declare parameters and provide parameter values
Do the work for each iteration.
I'm trying to do a simple thing: call stored procedure which have a object type parameter.
This is what I have in db:
create or replace
TYPE TEST_TYPE AS OBJECT
(
test_field varchar(100)
)
and
CREATE OR REPLACE PROCEDURE TEST_PROC
(
PARAM1 IN TEST_TYPE
) AS
BEGIN
END TEST_PROC;
This is what I have in my java code:
#Embeddable
#Struct(name = "TEST_TYPE", fields = {"TEST_FIELD"})
public class TestStruct
{
private String testField;
public String getTestField() {
return testField;
}
public void setTestField(String testField) {
this.testField = testField;
}
}
and
#PostConstruct
public void init()
{
StoredProcedureCall call = new StoredProcedureCall();
call.setProcedureName("TEST_PROC");
call.addNamedArgument("PARAM1", "PARAM1", Types.STRUCT, "TEST_TYPE", TestStruct.class);
DataReadQuery dataReadQuery = new DataReadQuery(call);
dataReadQuery.addArgument("PARAM1");
TestStruct testStruct = new TestStruct();
List args = new ArrayList();
args.add(testStruct);
Object result = ((EntityManagerImpl)em.getDelegate()).getSession().executeQuery(dataReadQuery,args);
}
this is what I get in runtime:
Internal Exception: java.sql.SQLException: Invalid column type
Error Code: 17004
Call: BEGIN TEST_PROC(PARAM1=>?); END;
bind => [1 parameter bound]
Query: DataReadQuery()
I think I totally don't understand the subject of usage structs with JPA
please help me, good people :)
What is the shortest way to make this working?
Please send complete your code.
For call stored procedures using Spring, you have to extends StoredProcedure class. If you send your complete code, I can help better. sample pseudo code:
class CustomStoredProcedure extends org.springframework.jdbc.object.StoredProcedure
{
CustomStoredProcedure()
{
super([your-data-source], [package-name]);
declareParameter(new SqlParameter([your-struct-name]), Types.STRUCT));
compile();
}
Map<String, Object> execute([your-parameter])
{
return super.execute(inputs);
}
}
for better help, you have explain complete situation.
Your code looks correct.
Ensure that the descriptor was defined for the struct. (i.e. session.getDescrptor(TestStruct.class))
Can you call stored procedures with other types?
What database are you using, have you set your platform correctly to Oracle?
It seems that eclipselink skips descriptors for the #Struct and #Embeddable annotated classes unless they are referenced by some other class. The shortest way to make it working is to use workaround based on this assumption. Put additional class in the jar where your META-INF/persistence.xml is located:
#Entity
public class StructEntitiesWorkaround {
#Id
private String id;
private TestStruct testStruct;
}
You might want to use SimpleJdbcCall with Types.STRUCT.
Here is an example: https://docs.spring.io/spring-data/jdbc/old-docs/2.0.0.M1/reference/html/orcl.datatypes.html