SQL Transactions and JDBC - postgresql

I have a method that does two things in one transaction:
1- Changes the state of the clan_gold column in the clan table. It depends on which of the clans won.
2- Saves the Event object to the events table. When saving an Event, among other things, the state of clan_gold of the current clan is fixed before and after the changes.
public class ActionRepository {
private final Connection connection = ConnectionManager.open();
{
try {
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void actionWithEnemy(int bet, Long enemyClanId, Event event) {
try (PreparedStatement preparedStatement =
connection.prepareStatement("BEGIN;"
+ "UPDATE clans SET clan_gold=(SELECT clan_gold FROM clans WHERE clan_id=?) + ? WHERE clan_id=?;"
+ "UPDATE clans SET clan_gold=(SELECT clan_gold FROM clans WHERE clan_id=?) - ? WHERE clan_id=?;"
+ "INSERT INTO events (user_name, clan_name, enemy_clan_name, type, success, gold_before, gold_after, date_time)"
+ "VALUES (?, ?, ?, ?, ?, (SELECT clan_gold FROM clans WHERE clan_id=?), (SELECT clan_gold FROM clans WHERE clan_id=?) + ?, current_timestamp);"
+ "COMMIT;")) {
preparedStatement.setLong(1, CurrentUser.getCurrentUser().getClan().getId());
preparedStatement.setInt(2, bet);
preparedStatement.setLong(3, CurrentUser.getCurrentUser().getClan().getId());
preparedStatement.setLong(4, enemyClanId);
preparedStatement.setInt(5, bet);
preparedStatement.setLong(6, enemyClanId);
preparedStatement.setString(7, event.getUserName());
preparedStatement.setString(8, event.getClanName());
preparedStatement.setString(9, event.getEnemyClanName());
preparedStatement.setString(10, event.getEventType().toString());
preparedStatement.setBoolean(11, event.isSuccess());
preparedStatement.setLong(12, CurrentUser.getCurrentUser().getClan().getId());
preparedStatement.setLong(13, CurrentUser.getCurrentUser().getClan().getId());
preparedStatement.setInt(14, bet);
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
But the Event object persists unpredictably to me.
I expect the gold_before value of each following line to match the gold_after value of the previous line. But it is not always the case.
It occurs both at start of one flow and .
The total amount of gold of all clans always remains the same, which means there is no data loss.
I am using postgres

Related

How can I read 23 million records from postgres using JDBC? I have to read from a table in postgres and write to another table

When I write a simple JPA code to findAll() data, I run into memory issues. For writing, I can do batch update. But how to read 23 million records and save them in list for storing into another table?
Java is a poor choice for processing "batch" stuff (and I love java!).
Instead, do it using pure SQL:
insert into target_table (col1, col2, ...)
select col1, col2, ....
from ...
where ...
or, if you must do some processing in java that can't be done within the query, open a cursor for the query and read rows 1 at a time and write the target row before reading the next row. This approach however will take a looooong time to finish.
I fully agree with Bohemian's answer.
If the source and the destination tables you can read and write within the same loop
something in a try - catch block like:
PreparedStatement reader = null;
PreparedStatement writer = null;
ResultSet rs = null;
try {
reader = sourceConnection.prepareStatement("select....");
writer = destinationConnection.prepareStatement("insert into...");
rs = reader.executeQuery();
int chunksize = 10000; // this is you batch size, depends on your system
int counter = 0;
while ( rs.next() {
writer.set.... // do for every field to insert the corresponding set
writer.addBatch();
if ( counter++ % chunksize == 0 ) {
int rowsWritten = writer.executeBatch();
System.out.println("wrote " + counter + " rows"); // probably a simple message to see a progress
}
}
// when finished, do not forget to flush the rest of the batch job
writer.executeBatch();
} catch (SQLException sqlex ) {
// an Errormessage to your gusto
System.out.println("SQLException: " + sqlex.getMessage());
} finally {
try {
if ( rs != null ) rs.close();
if ( reader != null ) reader.close();
if ( writer != null ) writer.close();
// probably you want to clsoe the connections as well
} catch (SQLException e ) {
System.out.println("Exception while closing " + e.getMessage());
}
}

cursor count is 1 whereas the table has 3 rows

I m trying to populate sql table and then retrieve data from it. Following is my code.
public void addQuestion(Question quest)
{
int id = 1;
ContentValues values = new ContentValues();
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("DROP TABLE IF EXISTS " + TABLE_QUEST1);
onCreate(db);
values.put(KEY_QUES, quest.getQuestion());
values.put(KEY_ANSWER, quest.getAnswer());
values.put(KEY_OPTA, quest.getOptA());
values.put(KEY_OPTB, quest.getOptB());
values.put(KEY_OPTC, quest.getOptC());
db.insert(TABLE_QUEST1, null, values);
System.out.println("Added in database: " + quest.getQuestion());
}
public ArrayList<Question> getAllQuestions() {
System.out.println("getting rows 1");
ArrayList<Question> quesList = new ArrayList<Question>();
System.out.println("getting rows 2");
Cursor cursor = null;
SQLiteDatabase db = getReadableDatabase();
System.out.println("getting rows ");
cursor = db.rawQuery("SELECT * FROM " + TABLE_QUEST1, null);
if (!cursor.moveToFirst()) {
System.out.println("No data in the database ");
} else {
System.out.println("theres data in the database ");
quesList = new ArrayList<Question>();
do {
System.out.print("total rows " + cursor.getCount());
Question quest = new Question();
quest.setID(cursor.getInt(0));
quest.setQuestion(cursor.getString(1));
quest.setAnswer(cursor.getString(2));
quest.setOptA(cursor.getString(3));
quest.setOptB(cursor.getString(4));
quest.setOptC(cursor.getString(5));
quesList.add(quest);
} while (cursor.moveToNext());
cursor.close();
}
}
I have 4 rows of data in my table and I can see that with the print statement "added in database"
but when i actually read it the cursor just reads row 1 and moves out of the while loop. what could potentially be wrong.
tia
Your code was absolutely fine except placing drop command in the loop. As mentioned in the earlier comments, please make sure to avoid calling drop query each time and you'll find the result.
As Santosh has pointed out DROPPING the table (as per db.execSQL("DROP TABLE IF EXISTS " + TABLE_QUEST1);) and then re-creating it (as per onCreate(db);) will delete the table and then re-create the table removing any rows/data that had previously been added to the table.
As such it's simply a matter of removing those two lines of code, Also there appears to be no need for the line int id = 1;, so perhaps remove this, as per :-
public void addQuestion(Question quest)
{
ContentValues values = new ContentValues();
SQLiteDatabase db = this.getWritableDatabase();
values.put(KEY_QUES, quest.getQuestion());
values.put(KEY_ANSWER, quest.getAnswer());
values.put(KEY_OPTA, quest.getOptA());
values.put(KEY_OPTB, quest.getOptB());
values.put(KEY_OPTC, quest.getOptC());
db.insert(TABLE_QUEST1, null, values);
System.out.println("Added in database: " + quest.getQuestion());
}
P.S. you may consider not using hard coded column offsets but instead obtain offsets according to column names by utilising the getColumnIndex(column_name) Cursor method. e.g. :-
Question quest = new Question();
quest.setID(cursor.getInt(cursor.getColumnIndex("name_of_your_id_columm")));
quest.setQuestion(cursor.getString(cursor.getColumnIndex(KEY_QUES)));
quest.setAnswer(cursor.getString(cursor.getColumnIndex(KEY_ANSWER)));
quest.setOptA(cursor.getString(cursor.getColumnIndex(KEY_OPTA)));
quest.setOptB(cursor.getString(cursor.getColumnIndex(KEY_OPTB)));
quest.setOptC(cursor.getString(cursor.getColumnIndex(KEY_OPTC)));
quesList.add(quest);
Noting that instead of "name_of_your_id_columm", you may have something like KEY_ID defined, if so use that, thus you have a single definition so it reduces the chance of inadvertently mispelling column names or miscalculating the offsets.

How do I replicate %Dictionary.ClassDefintionQuery's Summary() in SQL?

There is a procedure in %Dictionary.ClassDefinitionQuery which lists a class summary; in Java I call it like this:
public void readClasses(final Path dir)
throws SQLException
{
final String call
= "{ call %Dictionary.ClassDefinitionQuery_Summary() }";
try (
final CallableStatement statement = connection.prepareCall(call);
final ResultSet rs = statement.executeQuery();
) {
String className;
int count = 0;
while (rs.next()) {
// Skip if System is not 0
if (rs.getInt(5) != 0)
continue;
className = rs.getString(1);
// Also skip if the name starts with a %
if (className.charAt(0) == '%')
continue;
//System.out.println(className);
count++;
}
System.out.println("read: " + count);
}
}
In namespace SAMPLES this returns 491 rows.
I try and replicate it with a pure SQL query like this:
private void listClasses(final Path dir)
throws SQLException
{
final String query = "select id, super"
+ " from %Dictionary.ClassDefinition"
+ " where System = '0' and name not like '\\%%' escape '\\'";
try (
final PreparedStatement statement
= connection.prepareStatement(query);
final ResultSet rs = statement.executeQuery();
) {
int count = 0;
while (rs.next()) {
//System.out.println(rs.getString(1) + ';' + rs.getString(2));
count++;
}
System.out.println("list: " + count);
}
}
Yet when running the program I get this:
list: 555
read: 491
Why are the results different?
I have looked at the code of %Dictionary.ClassDefinitionQuery but I don't understand why it gives different results... All I know, if I store the names in sets and compare, is that:
nothing is missing from list that is in read;
most, but not all, classes returned by list which are not in read are CSP pages.
But that's it.
How I can I replicate the behaviour of the summary procedure in SQL?
Different is in one property. %Dictionary.ClassDefinitionQuery_Summary shows only classes with Deployed<>2. So, sql must be such.
select id,super from %Dictionary.ClassDefinition where deployed <> 2
But one more things is, why count may be different is, such sql requests may be compilled to temporary class, for example "%sqlcq.SAMPLES.cls22"

Insert Data using JDBC to a MySQL database through a method

I have a form and I want to send the data to a database. How can I pass the request data through the method parameters and send it to the Database?
int status = InsertCustomer(fName, mName, lName , iage, issn, city, state, country);
//Method
// This method should return an int that the executeUpdate       // methods returns. Note: the driver name and the URL are       // already available in the init() method.
private int InsertCustomer(String firstName, String midName, String lastName, int age, int ssn, String city, String state, String country) {
// JDBC logic
try {
Class.forName(driverName);
Connection conn = DriverManager.getConnection(databaseURL);
java.sql.Statement st = conn.createStatement();
st.executeUpdate("INSERT INTO Customer(firstName, midName, lastName, age, ssn, city, state, country)" +
"VALUES ('?', '?', '?', ?, ?, '?', '?', '?')";
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return 1;
}
I'm a lil bit lost, a little would be very much appreciated.
Either you use a simple (i.e. not prepared statement), and you can't pass any parameter:
String sql = "insert into sometable (a, b, c) values (7, 8, 9)";
Statement st = conn.createStatement();
return st.executeUpdate(sql);
Or (and in your case, that's what you should do), you use a prepared statament and pass parameters:
String sql = "insert into sometable (a, b, c) values (?, ?, ?)";
PreparedStatement st = conn.prepareStatement(sql);
st.setInt(1, 7);
st.setInt(2, 8);
st.setInt(3, 9);
return st.executeUpdate();
In your code, you're using a simple statement and try to execute a SQL query which needs parameters. That's not possible. You need a prepared statement to do that.
More information in the JDBC tutorial.

How to Count the number of rows in Sparql Query

I am working in Eclipse and am using 2 Java Files: Admin.java and SemanticSearch.java. Through the the Admin.java I am logging in and checking if the Username and Password are existing in my RDF file. The function of login in Admin.java calls SemanticSearch.java which runs a SPARQL Query. My Query is giving me the answer in Console of Eclipse and even onto another file. Now my job is to give back the answer to Admin.java either by returning the value or by counting rows and sending that value to Admin.java. With this if number of rows is 1 that means the username and password match and I can allow the user to login.
But I am not able to do so. I have tried using count(), Count() as CNT, even tried int res=results.next. But nothing seems to help.
I am pasting the code below:
Admin.java
SemanticSearch semsearch = new SemanticSearch(request.getSession());
semsearch.loadData(REALPATH + RDFDATASOURCEFILE1);
semsearch.searchForUser(response.getOutputStream(),null, userName, password);
In SemanticSearch.java
public void searchForUser(OutputStream out, String xslfile1, String userName, String password) {
String prolog = "PREFIX kb:<"+VUSER.getURI()+">";
System.out.println("Search for user in semantic search.java");
String queryString1 = prolog +"\n" +"SELECT * " +"WHERE {" +"?x kb:Uname ?username. ?x kb:Password ?password. ?x kb:Interest ?interest. " +"FILTER regex(?username, \"" +userName +"\")}";
System.out.println(queryString1);
Query query=QueryFactory.create(queryString1);
QueryExecution qexec = QueryExecutionFactory.create(query, model);
ResultSet results1 = qexec.execSelect(); --> here the two rows are printed
int res=results1.getRowNumber();
System.out.println(results1); -->here answer is 0
ResultSetFormatter.out(results1);
ResultSetFormatter.out(out, results1);
System.out.println(res);
try {
if (xslfile1 != null)
{
out.write(ResultSetFormatter.asXMLString(results1, xslfile1).getBytes("UTF-8"));
System.out.println(results1);
System.out.println(xslfile1);
System.out.println("I am in if");
}
else
{
out.write(ResultSetFormatter.asXMLString(results1).getBytes(
"UTF-8"));
System.out.println("I am in else");
}
} catch (IOException e) {
e.printStackTrace();
}
}
Please Help, I am struggling to get this from a week, Regards, Archana.
In SPARQL 1.1, which may be supported in the triplestore you're using, the syntax is:
SELECT (COUNT(*) AS ?count)
WHERE {
...
}
You should get an integer returned in the ?count column, with the number of results.