Stored procedure using hibernate - java

I want to use stored procedure to add and delete hierarchical data using hibernate. For doing that I want to first check if that procedure exist in database and if not create it. I looked for the standard way of doing this in hibernate but found nothing. I am just curious to know that is it good to create a procedure using hibernate or is there a better way of doing operation on hierarchical data in hibernate.
I am calling webservice from my app that is using the stored procedure to return data in a hierarchical format. If the procedure is deleted at runtime unknowingly, my app will never be able to retrieve data. So what I want if procedure does not exist create it. I am not sure is it the right way or not?
I need guidance...

You can call stored function or procedure in Hibernate as follow,
session.doWork(new Work() {
public void execute(Connection connection) throws SQLException {
CallableStatement call = connection.prepareCall("{ ? = call MYSCHEMA.MYFUNC(?,?) }");
call.registerOutParameter( 1, Types.INTEGER ); // or whatever it is
call.setLong(2, id);
call.setLong(3, transId);
call.execute();
int result = call.getInt(1); // propagate this back to enclosing class
}
});
Similarly
int result = session.doReturningWork(new ReturningWork<Integer>() {
#Override
public Integer execute(Connection connection) throws SQLException {
CallableStatement call = connection.prepareCall("{ ? = call MYSCHEMA.MYFUNC(?,?) }");
call.registerOutParameter( 1, Types.INTEGER ); // or whatever it is
call.setLong(2, id);
call.setLong(3, transId);
call.execute();
return call.getInt(1); // propagate this back to enclosing class
}
});

Related

Jooq Converter Cast Exceptions

I'm currently trying to store encrypted data in some of the columns of a Postgres DB. After receiving helpful feedback from this question: client-side-encryption-with-java-and-postgres-database I am using converters/bindings to implement transparent encryption in the JDBC layer.
Right now I'm trying to insert a BigDecimal[][][] into a Postgres DB column of type bytea.
The insertion works but the problem is that the encryption code I've added in the converters/binding doesn't seem to run. Unfortunately, when I check the database I'm seeing an unencrypted 3D matrix. (FYI my encryption utility code is tested and does work)
To test, I put my encryption code in the DAO layer and the BigDecimal[][][] matrix does get encrypted on DB inserts. Although I could do this it defeats the purpose of using converters/bindings for encryption.
So my question:
With the code I provided below am I doing anything wrong that is preventing the encryption code in my converter/binding to be run? I thought after a Prepared Statement is executed the converter is the next step but maybe not? I have a lack of knowledge on just when the converter/binding code gets called in the whole JOOQ flow so any insight is much appreciated! Thanks :D
First I'm using a PreparedStatment in a DAO to execute the insert query.
I can't show the full code but basically for the stmt I'm setting the BigDecimal[][][] as an object parameter:
private Result executeInsert(BigDecimal[][][] valueToEncrypt, String insertSql) {
try (Connection conn = config.connectionProvider().acquire();
PreparedStatement stmt = conn.prepareStatement(insertSql)) {
// Get a human readable version of the 3d matrix to insert into the db.
PostgresReadableArray humanReadableMatrix = getPostgresReadableArray(valueToEncrypt)
stmt.setObject(parameterIndex++, humanReadableMatrix, Types.OTHER);
ResultSet res = stmt.executeQuery();
}
...
}
I am currently attaching the binding to a codegen xml file here:
<forcedType>
<userType>
java.math.BigDecimal[][][]
</userType>
<binding>com.myapp.EncryptionBinding</binding>
<includeExpression>matrix_column</includeExpression>
<includeTypes>bytea</includeTypes>
</forcedType>
Here is my binding class EncryptionBinding:
public class EncryptionBinding implements Binding<byte[], BigDecimal[][][]> {
#Override
public Converter<byte[], BigDecimal[][][]> converter() {
return new MatrixConverter();
}
// Rending a bind variable for the binding context's value and casting it to the json type
#Override
public void sql(BindingSQLContext<BigDecimal[][][]> ctx) throws SQLException {
}
// Registering VARCHAR types for JDBC CallableStatement OUT parameters
#Override
public void register(BindingRegisterContext<BigDecimal[][][]> ctx) throws SQLException {
ctx.statement().registerOutParameter(ctx.index(), Types.VARCHAR);
}
// Converting the BigDecimal[][][] to a Encrypted value and setting that on a JDBC PreparedStatement
#Override
public void set(BindingSetStatementContext<BigDecimal[][][]> ctx) throws SQLException {
ctx.statement().setBytes(ctx.index(), ctx.convert(converter()).value());
}
...
Here is my converter class MatrixConverter used in the above EncryptionBinding class:
public class MatrixConverter extends AbstractConverter<byte[], BigDecimal[][][]> {
private static final Logger logger = LoggerFactory.getLogger(MatrixConverter.class);
public MatrixConverter() {
super(byte[].class, BigDecimal[][][].class);
}
#Override
public BigDecimal[][][] from(byte[] databaseObject) {
return EncryptionUtils.decrypt(databaseObject);
}
#Override
public byte[] to(BigDecimal[][][] userObject) {
return EncryptionUtils.encrypt(JsonUtils.toJson(userObject));
}
}

How can I execute batch stored procedure call from java code?

I have an procedure in oracle database which takes two parameters:
procedure some_procedure(int x, int y)
My project is using spring boot + hibernate. So, the question is how I can execute this stored procedure using batching (for example 100) from java code?
I've found some examples with usage of #Procedure annotation and also with StoredProcedureQuery - but those were only for simple not batching call.
And also could someone explain when I should use #Procedure annotation instead of StoredProcedureQuery?
When calling stored procedure, you get OUT parameters and can,t change it through calling. If stored procedure return ref_cursor, and you want to limit records, change stored procedure.
With StoredProcedureQuery you will write more code and manually manage calling process (get entityManager, open transaction, make a call, close transaction ...). With JPA repository you describe procedure by annotations and go. But both approaches work.
UPD:
Oh, little misunderstanding. You can call procedure several times in one transaction with StoredProcedureQuery, is this what you want? Code looks like this:
public class ProcedureCall {
#Autowired
private EntityManagerFactory entityManagerFactory;
public void executeProcedureWithoutResult(Map<String, ?> parameters, String procedureName) {
EntityManager em = entityManagerFactory.createEntityManager();
StoredProcedureQuery procedureQuery1 = em.createNamedStoredProcedureQuery(procedureName);
StoredProcedureQuery procedureQuery2 = em.createNamedStoredProcedureQuery(procedureName);
fillProcedureParameters(procedureQuery1, parameters);
fillProcedureParameters(procedureQuery2, parameters);
try {
em.getTransaction().begin();
procedureQuery1.execute();
procedureQuery2.execute();
em.getTransaction().commit();
} catch (Exception e) {
rollbackTransaction(e, em.getTransaction());
} finally {
if (em.isOpen()) {
em.close();
}
}
}
private void fillProcedureParameters(StoredProcedureQuery sp, Map<String, ?> parameters) {
if (parameters.size() > 0) {
for (HashMap.Entry<String, ?> rec : parameters.entrySet()) {
sp.setParameter(rec.getKey(), rec.getValue());
}
}
}
Maybe it even works, if you execute one procedureQuery twice, but I didn't try.

How to remove duplication from CallableStatement boiler plate code?

We have many stored procedures and functions that we call on our DB and the setup for each call in our data access layer is really verbose with setting the inputs and registering the output etc... Is there a better solution to maybe generating the CallableStatement dynamically for any stored procedure or function with any types/amounts of parameters and output type?
We have a home brew solution and it is ugly... full of if/else, fors and whiles... very hard to read and maintain. We have also tried to centralize common boilerplate code for like function calls. I.E. All of the ones that take a Long and return a boolean, all use the same centralized method with dynamic Long and stored procedure string.
The code is from memory please don't pay too much attention to syntax, this is a design question more than anything.
//Client usage in Controller class
certAwarded = PackageName.isCertAwardedFor(personIDToCheck);
//In class that mimics the interface of the database packages
//There would be a method per public function
public static boolean isCertAwardedFor(Long personID){
return PackageUtils.isMet(personID, "{? = call PACKAGE.is_met(?)}");
}
//In Package scoped Utility class
//Attempt to centralize all single input param and return of boolean
//type of procedure calls.
static boolean isMet(Long personID, String proc){
boolean met = false;
try(AutoCloseableStatement stmt = new AutoCloseableStatement(proc)){
CallableStatement callableStmt = stmt.createStatement();
callableStmt.registerOutParameter(1, OracleTypes.VARCHAR2);
callableStmt.setLong(2, personID);
callableStmt.execute();
met = convertYNtoBool(callableStmt.getString(1));
}catch(SQLException ex){
Logger.log(ex);
}
return met;
}
///////////////////////////////////OR///////////////////////////////
//Client usage in Controller class
certAwarded = PackageName.isCertAwardedFor(personIDToCheck, CertPackageEnum);
//In class that mimics the interface of the database packages
//There would be a method per public function
public static boolean isCertAwardedFor(Long personID, PackageProc procCall){
return PackageUtils.call(personID, procCall.IS_CERT_AWARDED);
}
//In Package scoped Utility class
//Attempt to centralize all single input param and return of boolean
//type of procedure calls.
static boolean isMet(Long personID, String proc){
try(AutoCloseableStatement stmt = new AutoCloseableStatement(proc)){
CallableStatement callableStmt = stmt.createStatement();
LOTS OF CONDITIONS TO CHECK AND SET ALL POSSIBLE INPUTS AND OUTPUTS
}catch(SQLException ex){
Logger.log(ex);
}
return ?????
}

Stream very large table via spring-data-jdbc Resultset.TYPE_FORWARD_ONLY READ_ONLY

Hi I would like to stream a very large table spring-data-jdbc. For this purpose
I have set my connection to READ_ONLY I have declared in my repository a method that looks in the following way:
PackageRepository extends Repository<Package,String> {
Stream<Package> findAll();
}
My expectation here would be that the resultset would be of type FORWARD_ONLY and this method will not block indefinatly untill all results are recieved from the database.
Here I would make a comparison with Spring Data JPA where the Stream methods are not blocking and the content of the database is fetched in portions depending on the fetch size.
Have I missed some configuration ? How can I achieve this behaviour with spring-data-jdbc ?
UPDATE: I will put the question in a different form. How can I achieve with spring-data-jdbs the equivalent of:
template.query(new PreparedStatementCreator() {
#Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement statement = con.prepareStatement("select * from MYTABLE with UR",ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
statement.setFetchSize(150000);
return statement;
}
}, new RowCallbackHandler() {
#Override
public void processRow(ResultSet rs) throws SQLException {
// do my processing here
}
});
Just adding thesetFetchSize(Integer.MIN_VALUE) before querying, the queryForStream indeed gives us a stream which load records one by one rather than eagerly load all records into memroy in one shot.
namedTemplate.getJdbcTemplate().setFetchSize(Integer.MIN_VALUE);
Stream<LargeEntity> entities = namedTemplate.queryForStream(sql, params, rowMapper);
dependencies:
spring framework 5.3+
mysql-connector-java 8.0.x (or mariadb-java-client 2.7.x)

Java: Prepare a statement without a connection

I'm trying to generate some sql files in my java application.
The application will not execute any sql statements, just generate a file with sql statements and save it.
I'd like to use the java.sql.PreparedStatement to create my statements so that i don't have to validate every string etc. with my own methods.
Is there a way to use the PreparedStatement without the calling java.sql.Connection.prepareStatement(String) function, because I don't have a java.sql.Connection?
Take a look at this Java library: http://openhms.sourceforge.net/sqlbuilder/
I'm guessing that until you've got a sql connection, the parser won't know what rules to apply. I'm guessing that it's actually the SQL driver or even server that's compiling the sql statement.
Assuming your sql is simple enough, then how about using a cheap connection, like, say a sqlite connection.
SQLite will create a new database on the fly if the database you're attempting to connect to does not exist.
public Connection connectToDatabase() {
// connect to the database (creates new if not found)
try {
Class.forName("org.sqlite.JDBC");
conn = DriverManager.getConnection("jdbc:sqlite:mydatabase.db");
// initialise the tables if necessary
this.createDatabase(conn);
}
catch (java.lang.ClassNotFoundException e) {
System.out.println(e.getMessage());
}
catch (java.sql.SQLException e) {
System.out.println(e.getMessage());
}
return conn;
}
Not really. Preparing a statement in most cases means that it will be compiled by DBMS which is "hard" without connection.
http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html
This is a dastardly devious problem, thankfully it's pretty easy to cope with:
public class PreparedStatementBuilder
{
private String sql; // the sql to be executed
public PreparedStatementBuilder(final String sql) { this.sql = sql; }
protected void preparePrepared(final PreparedStatement preparedStatement)
throws SQLException
{
// this virtual method lets us declare how, when we do generate our
// PreparedStatement, we want it to be setup.
// note that at the time this method is overridden, the
// PreparedStatement has not yet been created.
}
public PreparedStatement build(final Connection conn)
throws SQLException
{
// fetch the PreparedStatement
final PreparedStatement returnable = conn.prepareStatement(sql);
// perform our setup directives
preparePrepared(returnable);
return returnable;
}
}
To use, just write an anonymous class that overrides void preparePrepared(PreparedStatement):
final String sql = "SELECT * FROM FOO WHERE USER = ?";
PreparedStatementBuilder psBuilder = new PreparedStatementBuilder(sql){
#Override
protected void preparePrepared(PreparedStatement preparedStatement)
throws SQLException
{
preparedStatement.setString(1, "randal");
}};
return obtainResultSet(psBuilder);
Presto! You now have a way to work with a PreparedStatement without yet having built it. Here's an example showing the minimal boilerplate you'd otherwise have to copy paste to kingdom come, every time you wanted to write a different statement:
public ResultSet obtainResultSet(final PreparedStatementBuilder builder)
throws SQLException {
final Connection conn = this.connectionSource.getConnection();
try
{
// your "virtual" preparePrepared is called here, doing the work
// you've laid out for your PreparedStatement now that it's time
// to actually build it.
return builder.build(conn).executeQuery();
}
finally
{
try { conn.close(); }
catch (SQLException e) { log.error("f7u12!", e); }
}
}
You really really don't want to be copy pasting that everywhere, do you?
Try implementing PreparedStatement.
Example : class YourOwnClass implements PreparedStatement {
// 1. Do implement all the methods ,
2. Get the minimal logic to implement from OraclePreparedStatement(classes12.jar) or
sun.jdbc.odbc.JdbcOdbcCallableStatement
}

Categories