Create and use a stored procedure with Play Framework and JPA - jpa

I'm using Play framework 1.2.5 and I would like to optimize my SQL queries by creating stored procedures and using them but I don't know how to do.
To create the stored procedure via the Java code how should I do ? Also, should I do it in an #OnApplicationStart job so that I'm sure the procedures are created and stored when the application starts ?
After, how can I use my stored procedures ? Using which function ? How can I pass the parameters to my procedure ? How can I retrieve the result of my procedure ? (generally the result will be a SELECT query) And finally, is it possible to bind the result of my procedure to a model in the play framework ?
I have a lot of questions but I'm new to stored procedures with play framework and JPA and I would like to be sure I'm using them correctly
Thank you for your help

I don't know how you should create them. Perhaps the OnApplicationStart method is what you need. In my environment the procedures are already in place. We just use Play to invoke them. To invoke stored procedures, you should take a look at the Work interface. By implementing this you can execute statements in the database.
We've created a basic OracleProcedure class:
public class CallOracleProcedure implements Work {
private String anonymousPLSQL;
private String[] parameters;
public CallOracleProcedure(String anonymousPLSQL, String[] parameters) {
this.anonymousPLSQL = anonymousPLSQL;
this.parameters = parameters.clone();
}
/**
* Create a JDBC PreparedStatement and then execute the anonymous
* PL/SQL procedure.
*/
#Override
public void execute(Connection connection) {
PreparedStatement statement = null;
try {
statement = connection.prepareStatement("begin " + anonymousPLSQL + "; end;");
if (parameters != null) {
int i = 1;
for (String param : parameters) {
statement.setString(i++, param);
}
}
statement.executeUpdate();
} catch (SQLException e) {
Logger.error("Error performing anonymous pl/sql statement: '%s', with parameters: '%s' - catched error '%s'", anonymousPLSQL, parameters, e);
} finally {
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
Logger.error("Error closing statement: %s", e);
}
}
}
}
}
For each specific stored procedure you can extend this class and pass the name and parameters to the constructor via super():
public class StoredProcedureCall extends CallOracleProcedure {
public StoredProcedureCall(String param) {
super("package.storedprocedure(?)", new String[] { orgname });
}
}
In your code you can then call it like this:
StoredProcedureCall procedure = new StoredProcedureCall("your parameter");
session.doWork(procedure);
If you need to call a procedure and retrieve a return value you can use a CallableStatement in the execute() method:
public class ProcedureWithReturnValue implements Work {
private final String parameter;
private String returnValue = null;
public ProcedureWithReturnValue (final String parameter) {
this.parameter = parameter;
}
#Override
public void execute(Connection connection) {
CallableStatement statement = null;
try {
statement = connection.prepareCall("begin ? := package.procedure(?); end;");
statement.registerOutParameter(1, OracleTypes.VARCHAR);
statement.setString(2, parameter);
statement.execute();
returnValue = statement.getString(1);
} catch (SQLException e) {
Logger.error("Error getting return value - catched error '%s'", e);
}
}
public String getReturnValue() {
return returnValue;
}
}

Take a look at evolutions (http://www.playframework.com/documentation/1.2.7/evolutions) for creating your stored procedures.

Related

Query with parameters - AWS Amplify - Android

I am having trouble figuring out how to pass a parameter to a query. In this case, I want to get a user by name field. I know I can pass an id, but how do I pass another field? Do I need to create a secondary index?
private AWSAppSyncClient mAWSAppSyncClient;
mAWSAppSyncClient.query(GetUserQuery.builder().build())
.responseFetcher(AppSyncResponseFetchers.CACHE_AND_NETWORK)
.enqueue(userCallback);
query GetUser($id: ID!) {
getUser(id: $id) {
id
userId
name
...
}
}
Use ListUsers (ListXXXX) to query with multiple parameters.
Example - query by name = "aaa":
ModelStringFilterInput modelStringFilterInput = ModelStringFilterInput.builder().eq("aaa").build();
ModelUserFilterInput modelUserFilterInput = ModelUserFilterInput.builder().name(modelStringFilterInput).build();
mAWSAppSyncClient.query(ListUsersQuery.builder().filter(modelUserFilterInput).build())
.responseFetcher(AppSyncResponseFetchers.CACHE_AND_NETWORK)
.enqueue(userCallback);
private GraphQLCall.Callback<ListUsersQuery.Data> userCallback = new GraphQLCall.Callback<ListUsersQuery.Data>() {
#Override
public void onResponse(#Nonnull Response<ListUsersQuery.Data> response) {
Log.d(TAG, response.data().listUsers().items().toString());
}
#Override
public void onFailure(#Nonnull ApolloException e) {
Log.d(TAG, e.toString());
}
};

vertx-jdbc-client3.5.2 postgresql : sql update cause "A result was returned when none was expected"

In vertx-jdbc-client 3.5.0 ,the sql "DELETE FROM user;INSERT INTO user... " is ok.After update to vertx-jdbc-cient 3.5.2,the sql is fail with the error:
io.vertx.core.VertxException: org.postgresql.util.PSQLException: A result was returned when none was expected.By debugging, I found that the SQL was changed auto to DELETE FROM user RETURNING *;INSERT INTO user ..., then RETURNING * cause update error.
I find the cause:in vert-jdbc-client 3.5.0.
class JDBCConnectionImpl implements SQLConnection {
...
private SQLOptions options;
}
in vert-jdbc-client 3.5.2:
class JDBCConnectionImpl implements SQLConnection {
...
private SQLOptions options = new SQLOptions().setAutoGeneratedKeys(true);
}
then casue
public class Parser {
...
private static boolean addReturning(StringBuilder nativeSql, SqlCommandType currentCommandType,
String[] returningColumnNames, boolean isReturningPresent) throws SQLException {
if (isReturningPresent || returningColumnNames.length == 0) {
return false;
}
if (currentCommandType != SqlCommandType.INSERT
&& currentCommandType != SqlCommandType.UPDATE
&& currentCommandType != SqlCommandType.DELETE) {
return false;
}
nativeSql.append("\nRETURNING ");
...
}
Should I modify the SQL code or set up the JDBC client? Can anyone give me a suggestion?
I just found it's not a issue,because when i code SQLConnection sqlConnection = ...; sqlConnection.setOptions(null); ,then old sql update is ok.So it's a change,i think.

Review of Connection handling and Data access layer using C#, sql server compact 3.5

I am developing a stand alone application, using sql server compact 3.5 sp2 which runs in process. No Database writes involved. Its purely a reporting application. Read many articles about reusing open db connections in case of sql compact(connection pooling) due to its different behavior from sql server.
Quoting the comments from a quiz opened by Erik Ejlskov Jensen Link, where its discussed an open early close late strategy for sql server compact databases. Based on this, with my limited experience I have implemented a not so complex Connection handling+Data access layer. Basically I am unsure if i am writing it in a recommended way. Please could any one point me in the right direction with rooms for improvement in this connection handling approach i have written?
The DbConnection class
public class FkDbConnection
{
private static SqlCeConnection conn;
private static DataTable table;
private static SqlCeCommand cmd;
~FkDbConnection() { conn = null; }
//This will be called when the main winform loads and connection will be open as long as the main form is open
public static string ConnectToDatabase()
{
try {
conn = new SqlCeConnection(ConfigurationManager.ConnectionStrings["Connstr"].ConnectionString);
if (conn.State == ConnectionState.Closed || conn.State == ConnectionState.Broken)
{
conn.Open();
}
return "Connected";
}
catch(SqlCeException e) { return e.Message; }
}
public static void Disconnect()
{
if (conn.State == ConnectionState.Open || conn.State == ConnectionState.Connecting || conn.State == ConnectionState.Fetching)
{
conn.Close();
conn.Dispose();
//conn = null; //does conn have to be set to null?
}
//else the connection might be already closed due to failure in opening it
else if (conn.State == ConnectionState.Closed) {
conn.Dispose();
//conn = null; //does conn have to be set to null?
}
}
/// <summary>
/// Generic Select DataAccess
/// </summary>
/// <param name="sql"> the sql query which needs to be executed by command object </param>
public static DataTable ExecuteSelectCommand(SqlCeCommand comm)
{
if (conn != null && conn.State == ConnectionState.Open)
{
#region block using datareader
using (table = new DataTable())
{
//using statement needed for reader? Its closed below
using (SqlCeDataReader reader = comm.ExecuteReader())
{
table.Load(reader);
reader.Close(); //is it needed?
}
}
#endregion
# region block using dataadpater
//I read DataReader is faster?
//using (SqlCeDataAdapter sda = new SqlCeDataAdapter(cmd))
//{
// using (table = new DataTable())
// {
// sda.Fill(table);
// }
//}
#endregion
//}
}
return table;
}
/// <summary>
/// Get Data
/// </summary>
/// <param name="selectedMPs"> string csv, generated from a list of selected posts(checkboxes) from the UI, which forms the field names used in SELECT </param>
public static DataTable GetDataPostsCars(string selectedMPs)
{
DataTable dt;
//i know this it not secure sql, but will be a separate question to pass column names to select as parameters
string sql = string.Format(
"SELECT " + selectedMPs + " "+
"FROM GdRateFixedPosts");
using (cmd = new SqlCeCommand(sql,conn))
{
cmd.CommandType = CommandType.Text;
//cmd.Parameters.Add("#fromDateTime",DbType.DateTime);
//cmd.Parameters.Add("#toDateTime",DbType.DateTime);
dt = ExecuteSelectCommand(cmd);
}
return dt;
}
}
The Main UI (Form) in which connection opened, for connection to be open through out. 2 other reporting forms are opened from here. Closing main form closes all, at which point connection is closed and disposed.
private void FrmMain_Load(object sender, EventArgs e)
{
string str = FkDbConnection.ConnectToDatabase();
statStDbConnection.Items[0].Text = str;
}
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
FkDbConnection.Disconnect();
}
Comments, improvements on this connection class much appreciated. See my questions also inline code
Thank you.
Updated classes as per Erik's suggestion. with a correction on ExecuteSelectCommand() and an additional class which will instantiate command objs in "using" and pass data to the UI. I intent to add separate GetDataForFormX() methods since the dynamic sql for each form may differ. Hope this is ok?
Correction to Erik's code:
public static DataTable ExecuteSelectCommand(SqlCeCommand comm)
{
var table = new DataTable();
if (conn != null && conn.State == ConnectionState.Open)
{
comm.Connection = conn;
using (SqlCeDataReader reader = comm.ExecuteReader())
{
table.Load(reader);
}
}
return table;
}
New FkDataAccess class for passing Data to UI
public class FkDataAccess
{
public static DataTable GetDataPostsCars(string selectedMPs)
{
var table = new DataTable();
string sql = string.Format(
"SELECT " + selectedMPs + " " +
"FROM GdRateFixedPosts");
if (FkDbConnection.conn != null && FkDbConnection.conn.State == ConnectionState.Open)
{
using (SqlCeCommand cmd = new SqlCeCommand(sql, FkDbConnection.conn))
{
cmd.CommandType = CommandType.Text;
//cmd.Parameters.Add("#fromDateTime",DbType.DateTime);
table = FkDbConnection.ExecuteSelectCommand(cmd);
}
}
return table;
}
//public static DataTable GetDataXY(string selectedvals)
// and so on
}
Too much code in your data access class, makes it unreadable and hard to maintain
The SqlCeonnection object will be disposed when you close it (and when the app closes)
You cannot dispose the DataTable if you want to use it elsewhere, and it is an completely managed object anyway.
It is a good pattern to limit your classes to a single responsibility
public class FkDbConnection
{
private static SqlCeConnection conn;
~FkDbConnection() { conn = null; }
//This will be called when the main winform loads and connection will be open as long as the main form is open
public static void ConnectToDatabase()
{
// Handle failure to open in the caller
conn = new SqlCeConnection(ConfigurationManager.ConnectionStrings["Connstr"].ConnectionString);
conn.Open();
}
public static void Disconnect()
{
if (conn != null)
{
conn.Close();
}
}
public static DataTable ExecuteSelectCommand(SqlCeCommand comm)
{
var table = new DataTable();
if (conn != null && conn.State == ConnectionState.Open)
{
comm.Connection = conn;
using (SqlCeDataReader reader = comm.ExecuteReader())
{
table.Load(reader);
}
}
return table;
}
private void FrmMain_Load(object sender, EventArgs e)
{
try
{
FkDbConnection.ConnectToDatabase();
statStDbConnection.Items[0].Text = "Connected";
}
catch (Exception ex)
{
//Inform use that we canot proceed, what she can do to remedy, and exit
}
}
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
FkDbConnection.Disconnect();
}

The column name ... was not found in this ResultSet

We are using java jdk 1.7.0_45, postgresql jdbc connector postgresql-9.3-1100.jdbc41.jar.
Here is a synopsis of our problem, as much as possible of code pasted below.
This code:
ResultSet rs = DbConn.getInstance().doQuery("Select d.deptId from Depts d");
while (rs.next()){
System.out.println(rs.getInt("d.deptId"));
Produces the error:
org.postgresql.util.PSQLException: The column name d.deptId was not found in this ResultSet.
This code:
ResultSet rs = DbConn.getInstance().doQuery("Select d.deptId from Depts d");
while (rs.next()){
System.out.println(rs.getInt("deptId"));
Produces no error.
Is there a way, besides removing the "d." from the first query, to make the first code snippet not throw the error message?
Here is the source code:
public class JoinTest {
#Test
public void test(){
boolean pass = false;
try {
ResultSet rs = DbConn.getInstance().doQuery("Select d.deptId from Depts d");
String label = rs.getMetaData().getColumnLabel(1); // What do you get?
System.out.println("label = " + label);
while (rs.next()){
System.out.println(rs.getInt("d.deptId"));
pass = true;
}
} catch (SQLException e) {
e.printStackTrace();
pass=false;
}
assertTrue(pass);
}
#Test
public void test2(){
boolean pass = false;
try {
ResultSet rs = DbConn.getInstance().doQuery("Select d.deptId from Depts d");
while (rs.next()){
System.out.println(rs.getInt("deptId"));
pass = true;
}
} catch (SQLException e) {
e.printStackTrace();
pass=false;
}
assertTrue(pass);
}
}
public class DbConn {
private static String url = "jdbc:postgresql://server:port/schema";
private static Properties props = new Properties(); {
props.setProperty("user","userid");
props.setProperty("password","passwprd");
}
private Connection conn;
private DbConn(){}
private static DbConn instance;
public static DbConn getInstance() throws SQLException{
if (instance == null){
instance = new DbConn();
instance.conn = DriverManager.getConnection(url, props);
}
return instance;
}
public ResultSet doQuery(String query) throws SQLException{
Logger.log("DbConn.doQuery: " + query);
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(query);
return rs;
}
}
}
The query:
select d.deptId from Depts d
produces a single-column resultset with the result-alias "deptId". There is no "d.deptId" column. If you want one, you can request that as the column alias instead:
select d.deptId AS "d.deptId" from Depts d
PgJDBC can't do anything about this because it has no idea that the resultset column "deptId" is related to the "d.deptId" in the select-list. Teaching it about that would force it to understand way more about the SQL it processes than would be desirable, and lead to maintenance and performance challenges.
The second one works - why isn't that acceptable?
You can also do this:
System.out.println(rs.getInt(1));
If you change the query you have to change the code, too.

playframework1.2.3 save data into database without transaction

I cannot save my entity data into database without transaction.
I know PersistenceContextType.Extend, But I cannot success.
#NoTransaction
public class Application extends Controller {
public static void create(String body) {
// EntityTransaction tm = JPA.em().getTransaction();
if (!JPA.isEnabled()) {
System.out.println("JPA is not initialized");
}
EntityManager manager = JPA.entityManagerFactory.createEntityManager();
//manager.setFlushMode(FlushModeType.COMMIT);
manager.setProperty("org.hibernate.readOnly", false);
//new Customer("001").save();
if (!JPA.isInsideTransaction()) {
// manager.getTransaction().begin();
}
createContext(manager, false);
new Customer("001").save();
//manager.getTransaction().commit();
/*
* if (tm.equals(null)) { System.out.println("success"); }
*/
}
static void createContext(EntityManager entityManager, boolean readonly) {
if (JPA.local.get() != null) {
try {
JPA.local.get().entityManager.close();
} catch (Exception e) {
// Let's it fail
}
JPA.local.remove();
}
JPA context = new JPA();
context.entityManager = entityManager;
// context.readonly = readonly;
JPA.local.set(context);
}
}
I initialed the JPA by myself to prevent play from starting a transaction.
I want to save my data into database, but I get a TransactionRequiredException error.
I known that JPA operation need a transaction, but I want to know whether has a exception.
I am not really sure what you are trying to achieve here. It is best to let Play handle transactions. You will not be able to commit your changes without a transaction.
If you need more control as to when the transaction is commited you could use a utility method like:
public static void commit() {
if (JPA.em().getTransaction().getRollbackOnly()) {
JPA.em().getTransaction().rollback();
} else {
JPA.em().getTransaction().commit();
}
JPA.em().getTransaction().begin();
JPA.em().flush();
JPA.em().clear();
}