Stored Procedure caller; simply execute stored procedure from java code.
Compatible with jdk
>= 1.5
, with only single dependency (slf4j-api
)
Suppose you have 3 Stored Procedures to save customer to database, get customer by id and list all customer.
For example here's SP code using HSQL:
CREATE PROCEDURE create_customer(firstname VARCHAR(50), lastname VARCHAR(50), OUT custId INT,
OUT code SMALLINT, OUT msg VARCHAR(50))
MODIFIES SQL DATA DYNAMIC RESULT SETS 1
BEGIN ATOMIC
INSERT INTO CUSTOMERS VALUES (DEFAULT, firstname, lastname);
SET custId = IDENTITY();
SET code = 0 -- success;
END
CREATE PROCEDURE get_customer(IN custId INT, OUT firstname VARCHAR(50), OUT lastname VARCHAR(50),
OUT code SMALLINT, OUT msg VARCHAR(50))
READS SQL DATA
BEGIN ATOMIC
SELECT first_name, last_name INTO firstname, lastname FROM customers WHERE id = custId;
SET code = 0 -- success;
END
CREATE PROCEDURE list_customers(OUT code SMALLINT, OUT msg VARCHAR(50))
READS SQL DATA DYNAMIC RESULT SETS 1
BEGIN ATOMIC
DECLARE result CURSOR FOR SELECT id, first_name firstname, last_name lastname FROM CUSTOMERS;
OPEN result;
SET code = 0 -- success;
END
NOTE: Every Stored Procedure by default need to have 2 additional Output Parameters at the end of its parameter list. One of type
SMALLINT
and the other of typeVARCHAR
for result code and message respectively, where result code0
means success. You can override the0
value or remove this default behviour at all, see the configuration wiki page.
public interface CustomerDAO {
@StoredProc("create_customer")
void createCustomer(@Param(VARCHAR) String firstName, @Param(VARCHAR) String lastName);
@StoredProc("get_customer")
Customer getCustomer(@Param(INTEGER) Integer id);
@StoredProc("list_customers")
List<Customer> listCustomers();
}
Before start using the CustomerDAO
interface, one last step is required, to map the result of the get_customer
and list_customers
stored procedures.
get_customer
stored procs returns the result as Output Parameters, so you need to have a class to implementTypedOutputParamMapper
interface.list_customers
stored proc returns the result as Result Set, so you need to have a class to implementResultSetMapper
interface.
Let's create Customer class to implement both interfaces (for getCustomer
and listCustomers
):
public class Customer implements TypedOutputParamMapper<Customer>, ResultSetMapper<Customer> {
private Integer id;
private String firstName, lastName;
public Customer() {
}
public Customer(Integer id, String firstName, String lastName) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
public Integer id() {
return id;
}
public String firstName() {
return firstName;
}
public String lastName() {
return lastName;
}
//You can acess result set columns/output parameters by name as well
@Override
public Customer map(Result<?> result) {
if (result.isResultSet()) {
return new Customer(result.getInt(1), result.getString(2), result.getString(3));
} else {
return new Customer(null, result.getString(1), result.getString(2));
}
}
// for TypedOutputParamMapper
@Override
public List<Integer> getTypes() {
return Arrays.asList(VARCHAR, VARCHAR);
}
}
See more examples on spwrap-examples github project and read more about Mappers in the wiki.
NOTE: If your stored procedure returns a single output parameter with no result set, then you can use the
@Scalar
annotation and you will not need to provide a Mapper class yourself, the mapping will done for you. see wiki page about scalars for more
NOTE: You can use
@AutoMapper
s to do the mapping for you instead of Mapping the Result object into your domain object yourself.
Now you can start using the interface to call the stored procedures:
DataSource dataSource = ...
DAO dao = new DAO.Builder(dataSource).build();
CustomerDAO customerDao = dao.create(CustomerDAO.class);
customerDao.createCustomer("Abdullah", "Muhammad");
Customer customer = customerDao.getCustomer1(1);
Assert.assertEquals("Abdullah", customer.firstName());
Gradle:
compile group: 'com.github.mhewedy', name: 'spwrap', version: '0.0.20'
Maven:
<dependency>
<groupId>com.github.mhewedy</groupId>
<artifactId>spwrap</artifactId>
<version>0.0.20</version>
</dependency>
-
If you don't supply the stored procedure name to
@StoredProc
, it will use the method name by default. -
@Param
annotation should used for ALL method parameters and accepts the SQL Type perjava.sql.Types
. -
If you don't want to tie your Domain Object with
spwrap
as of step 3 above, you can have another class to implement the Mapper interfaces (TypedOutputParamMapper
andResultSetMapper
) and pass it to the annotaion@Mapper
like:
@Mapper(CustomResultSetMapper.class)
@StoredProc("list_customers")
List<Customer> listCustomers();
-
@Mapper
annotation overrides the mapping specified by the return type object, i.e.spwrap
extract Mapping infromation from the return type class, and then override it with the classes set by@Mapper
annotation if found. -
Your Stored procedure can return output parameter as well as One Result set in one call, to achieve this use
Tuple
return type:
@Mapper({MyResultSetMapper.class, MyOutputParameterMapper.class})
@StoredProc("list_customers_with_date")
Tuple<Customer, Date> listCustomersWithDate();
-
spwrap doesn't support INOUT parameters.
-
spwrap doesn't support returning multi-result sets from the stored procedure.
-
When the Stored procedure have input and output parameters, input parameters should come first and then the output parameters.
Because spwrap
is based on JDBC API, theoretically it should support any Database Management System with a JDBC Driver, However it is tested on HSQL, MySQL, SQL Server and Oracle with jdk 1.6, 1.7 and 1.8 (1.7 and 1.8 are remove to reduce build time). (Plan to test againest: Postgresql
, Sybase
, DB2
)
See wiki page for more info and test cases/spwrap-examples for more usage scenarios.