What is the recommended way to reset a C3P0 managed connection to its initial state?
I am using the Microsoft JDBC driver and setting a SET ROWCOUNT 1 on one connection. This results in all queries executed by that connection to return only one row even after the connection has been returned to the pool and later fetched again. Should I be resetting the values explicitly onCheckin or onCheckout?
Main class
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Connection;
import java.sql.ResultSet;
public class Main {
final ComboPooledDataSource cpds;
Main() throws PropertyVetoException {
cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.microsoft.sqlserver.jdbc.SQLServerDriver");
cpds.setJdbcUrl("jdbc:sqlserver://10.42.62.41:1433;databaseName=mach;integratedSecurity=false;SendStringParametersAsUnicode=true;applicationName=panda;");
cpds.setUser("testuser");
cpds.setPassword("welcome123");
cpds.setInitialPoolSize(0);
cpds.setMinPoolSize(1);
cpds.setMaxPoolSize(1);
cpds.setConnectionCustomizerClassName("C3p0ConnectionCustomizer");
cpds.setDescription("Netbeans test project");
}
Connection getConnection() throws SQLException{
return cpds.getConnection();
}
public static void main(String[] args) throws PropertyVetoException, SQLException {
Main m = new Main();
try(Connection connection = m.getConnection()){
Statement stmt = connection.createStatement();
stmt.execute("SET ROWCOUNT 1");
}
try(Connection connection = m.getConnection()){
try(Statement stmt = connection.createStatement()) {
int cnt = 0, rsCnt = 0;
boolean results = stmt.execute("select * from Foo; select * from Bar");
if(results) {
do {
rsCnt++;
ResultSet rs = stmt.getResultSet();
while(rs.next()) {
cnt++;
}
System.out.println(rsCnt + " -> " + cnt);
rs.close();
results = stmt.getMoreResults();
cnt = 0;
} while (results);
}
}
}
}
}
Customizer - Mainly to see the connection being used.
import com.mchange.v2.c3p0.AbstractConnectionCustomizer;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class C3p0ConnectionCustomizer extends AbstractConnectionCustomizer {
#Override
public void onAcquire(Connection c, String pdsIdt) {
try (Statement stmt = c.createStatement()) {
stmt.execute("SET ROWCOUNT 0");
} catch(SQLException sqle) {
sqle.printStackTrace();
}
}
#Override
public void onCheckOut(Connection c, String pdsIdt) {
System.out.println("Checked out " + c + " [" + pdsIdt + "]");
}
#Override
public void onCheckIn(Connection c, String pdsIdt) throws SQLException {
System.out.println("Checking in " + c + " [" + pdsIdt + "]");
}
}
Without the SET ROWCOUNT 1 line, more rows are returned by the above queries. The customizer logging shows that the same connection is being used.
One could argue that, ideally, code that calls SET ROWCOUNT 1 really should "clean up after itself" by making sure that it calls SET ROWCOUNT 0 before releasing the connection back to the pool.
However, if we cannot absolutely guarantee such behaviour then it seems quite reasonable to simply move your SET ROWCOUNT 0 call from the onAcquire method to the onCheckIn method. It would result in an extra round-trip for each check-in, even if resetting ROWCOUNT was not really necessary, but a SET ROWCOUNT 0 would be a relatively inexpensive operation.
(I just tried it with c3p0-0.9.5.2 and mssql-jdbc, and moving SET ROWCOUNT 0 to the onCheckIn method had the desired effect.)
Related
This is my first app on Android with Java and SQLite.
ISSUE:
I have a local SQLIte db on my app. I was very surprised to see how easy it is to get access to the db once you have installed the app (no need to be a programmer nor a hacker).
I tried adding SQLCipher to my app but it only worked for newer Android versions 11 & 12 and didn't work for Android 9 for example and it did make my app's size much bigger.
After researching more I found a better solution for my case which doesn"t involve crypting the db with SQLCipher but rather it consists of corrupting the first bytes of the db file then after each launch of the app the code will decorrupt the file and use the fixed file instead. This insures that anyone who decompiles the apk will only get access to a corrupt db file and will have to put more effort to fix it which is my goal.
I came across this solution in a reply [here][1] but I don't know how to implement it as I am new to Android and SQLite programming. Any help is much appreciated on how to actually do it.
These are the steps as mentioned by the user: farhad.kargaran which need more explanation as I don't get how to do it:
1- corrupt the db file (convert it to byte array and change some values)
2- copy it in asset folder
3- in first run fix corrupted file from asset and copy it in database
folder.
Change first 200 byte values like this:
int index = 0;
for(int i=0;i<100;i++)
{
byte tmp = b[index];
b[index] = b[index + 1];
b[index + 1] = tmp;
index += 2;
}
As only the first 200 bytes were replaced, the same code is used for fixing first 200 byte values.
Here is my code for the SQLiteOpenHelper if needed:
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = DatabaseHelper.class.getSimpleName();
public static String DB_PATH;
public static String DB_NAME;
public SQLiteDatabase database;
public final Context context;
public SQLiteDatabase getDb() {
return database;
}
public DatabaseHelper(Context context, String databaseName, int db_version) {
super(context, databaseName, null, db_version);
this.context = context;
DB_PATH = getReadableDatabase().getPath();
DB_NAME = databaseName;
openDataBase();
// prepare if need to upgrade
int cur_version = database.getVersion();
if (cur_version == 0) database.setVersion(1);
Log.d(TAG, "DB version : " + db_version);
if (cur_version < db_version) {
try {
copyDataBase();
Log.d(TAG, "Upgrade DB from v." + cur_version + " to v." + db_version);
database.setVersion(db_version);
} catch (IOException e) {
Log.d(TAG, "Upgrade error");
throw new Error("Error upgrade database!");
}
}
}
public void createDataBase() {
boolean dbExist = checkDataBase();
if (!dbExist) {
this.getReadableDatabase();
this.close();
try {
copyDataBase();
} catch (IOException e) {
Log.e(TAG, "Copying error");
throw new Error("Error copying database!");
}
} else {
Log.i(this.getClass().toString(), "Database already exists");
}
}
private boolean checkDataBase() {
SQLiteDatabase checkDb = null;
try {
String path = DB_PATH + DB_NAME;
checkDb = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
} catch (SQLException e) {
Log.e(TAG, "Error while checking db");
}
if (checkDb != null) {
checkDb.close();
}
return checkDb != null;
}
private void copyDataBase() throws IOException {
InputStream externalDbStream = context.getAssets().open(DB_NAME);
String outFileName = DB_PATH + DB_NAME;
OutputStream localDbStream = new FileOutputStream(outFileName);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = externalDbStream.read(buffer)) > 0) {
localDbStream.write(buffer, 0, bytesRead);
}
localDbStream.close();
externalDbStream.close();
}
public SQLiteDatabase openDataBase() throws SQLException {
String path = DB_PATH + DB_NAME;
if (database == null) {
createDataBase();
database = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READWRITE);
}
return database;
}
#Override
public synchronized void close() {
if (database != null) {
database.close();
}
super.close();
}
Much appreciated.
[1]: https://stackoverflow.com/a/63637685/18684673
As part of the copyDatabase, correct and then write the corrupted data, then copy the rest.
Could be done various ways
e.g.
long buffersRead = 0; //<<<<< ADDED for detecting first buffer
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = externalDbStream.read(buffer)) > 0) {
if (bufferesRead++ < 1) {
//correct the first 200 bytes here before writing ....
}
localDbStream.write(buffer, 0, bytesRead);
}
I am new in using TPL in .Net applications. While creating a simple console application to achieve some parallel tasks those are dynamically created, I am stuck with some issues.
Problem here is that when 10 tasks are created and run, although the console is showing all the 10 tasks, when writing those into a log file after putting a delay between consoling and logging, the log file misses some of the items randomly.
Below is my sample code (This is just a skeleton of my actual code)
class Program
{
public static int datacount = 10;
static void Main(string[] args)
{
List<Task> tasks = new List<Task>();
var s1 = DateTime.Now;
var transList = GenerateTransactionList();
foreach (var transaction in transList)
{
Transactions transactionNew = new Transactions();
transactionNew = transaction;
tasks.Add(Task.Factory.StartNew(() => serialMethod(transactionNew)));
}
Task.WhenAll(tasks).Wait();
Console.WriteLine("Completed!!!");
}
private static List<Transactions> GenerateTransactionList()
{
Random r = new Random();
List<Transactions> transactionList = new List<Transactions>();
for (int i = 1; i <= datacount; ++i)
{
Transactions tr = new Transactions();
tr.ID = 0;
tr.Amount = r.Next(1, 10);
tr.Created_By = "Iteration" + i;
tr.Notes = "Iteration" + i;
tr.Created_On = DateTime.Now;
transactionList.Add(tr);
}
return transactionList;
}
private static async Task<string> serialMethod(Transactions tlist)
{
Console.WriteLine("Started Serial Iteration" + tlist.Notes);
try
{
Console.WriteLine("Finished Serial Iteration" + tlist.Notes);
Thread.Sleep(10000);//doing some time consuming process
WriteLog("Parallel2", DateTime.Now, DateTime.Now, tlist.Notes);
return "Success";
}
catch (Exception ex)
{
Console.WriteLine("serialmethod" + ex.Message);
return "Failure";
}
}
public static void WriteLog(string type,
DateTime startTime, DateTime endTime,
string dataSet)
{
try
{
string logFolderPath = AppDomain.CurrentDomain.BaseDirectory + #"\Logs";
if (!Directory.Exists(logFolderPath))
Directory.CreateDirectory(logFolderPath);
string logFilePath = logFolderPath + #"\Log_" + DateTime.Today.ToString("yyyy.MM.dd") + ".csv";
string line = string.Empty;
if (!File.Exists(logFilePath))
{
line = #"""Type"",""Start Time"",""End Time"",""Duration"",""Iteration""";
writeLineToFile(logFilePath, line);
}
string duration = (endTime - startTime).ToString();
line = "\"" + type + "\"," +
"\"" + startTime.ToString("MM/dd/yyyy hh:mm:ss tt") + "\"," +
"\"" + endTime.ToString("MM/dd/yyyy hh:mm:ss tt") + "\"," +
"\"" + duration + "\"," +
"\"" + dataSet + "\"";
writeLineToFile(logFilePath, line);
}
catch (Exception)
{
//do nothing
}
}
private static void writeLineToFile(string fileName, string line)
{
using (var writer = new StreamWriter(fileName, true))
{
writer.WriteLine(line);
}
}
}
class Transactions
{
public int ID { get; set; }
public decimal Amount { get; set; }
public int Points { get; set; }
public string Notes { get; set; }
public string Created_By { get; set; }
public DateTime Created_On { get; set; }
}
Do you have any idea why this is happening. I have tried using ConcurrentBag instead of list. But that too is not helping. Please guide and let me know if I am missing anything or my implementation is completely wrong.
There a re a bunch of error-prone lines in your code:
You're overriding the reference for transaction in your foreach loop
You're using StartNew method instead of Tas.Run
You're using blocking WaitAll instead of await WhenAll, so you do block one thread in your application for no reason
You can simply switch to Parallel.Foreach instead of foreach
And most important: you're writing to the same file from different threads simultaneously, so they are basically interrupting each other. Either use some blocking to write the file (which cannot be done in parallel) or use some library for logging, like NLog or whatever, so it will handle logging for you
Your threads can run into situation when some of them trying to create file when other already done that, so move out the creation logic for file into one place (which the libraries like NLog will do for you properly)
Try to use object initializers instead of setting one property after another:
var tr = new Transactions
{
ID = 0,
Amount = r.Next(1, 10),
Created_By = "Iteration" + i,
Notes = "Iteration" + i,
Created_On = DateTime.Now
}
I am working on creating a program that reads strings in from a data file calles "palindromes.txt" and which stores the strings in a one-dimensional array. The program is supposed to inspect each of the strings and detrmine whether or not each one is a pallindrome. It's supposed to show output on the screen and into a file called "output.txt".
The problem i'm having is that I can't figure out how to get the input/output correct and i'm not sure the best way to utilize the arrays.
Very new to this, please advise if you can. :)
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class Palindromes2
{
public static boolean isPalindrome(String dirtystr)
{
dirtystr = dirtystr.toLowerCase().replaceAll("[^a-zA-Z]+","");
int length = dirtystr.length(), middle = length / 2;
for (int x = 0; x < middle; x++)
if (dirtystr.charAt(x) != dirtystr.charAt(length - x - 1))
return false;
return true;
}
public static void main(String[] args) throws IOException
{
File inputFile = new File("output.txt");
try (Scanner scanner = new Scanner(inputFile))
{
while (scanner.hasNextLine())
{
String dirtystr = scanner.nextLine();
if (isPalindrome(dirtystr))
{
System.out.println(dirtystr + " IS a palindrome.");
}
else
{
System.out.println(dirtystr + " is NOT a palindrome.");
}
}
}
}
}
Im trying to invoke a java program through C using JNI. The Java program is basically a JDBC connection to Hive.
Here is my code snippet
Java program
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.DriverManager;
public class HiveJdbcClient {
private static String driverName = "org.apache.hive.jdbc.HiveDriver";
/**
* #param args
* #throws SQLException
*/
public static void main(String[] args) throws SQLException {
try {
Class.forName(driverName);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.exit(1);
}
//replace "hive" here with the name of the user the queries should run as
Connection con = DriverManager.getConnection("jdbc:hive2://localhost:10000/default", "cloudera", "cloudera");
//Connection con = DriverManager.getConnection("jdbc:hive2://");
Statement stmt = con.createStatement();
String tableName = "testHiveDriverTable";
stmt.execute("drop table if exists " + tableName);
stmt.execute("create table " + tableName + " (key int, value string)");
String sql = "show tables '" + tableName + "'";
System.out.println("Running: " + sql);
ResultSet res = stmt.executeQuery(sql);
if (res.next()) {
System.out.println(res.getString(1));
}
}
}
and my C program looks like this
#include<stdio.h>
#include "jni.h"
int main ( void )
{
char cpath;
JNIEnv *env;
JavaVM *jvm;
int res;
jclass cls;
jmethodID mid = NULL;
//JavaVMOption options;
JavaVMOption options [3];
options[0].optionString="-Djava.class.path=desired-classpath";
options[1].optionString = "-verbose:class";
options[2].optionString = "-Xdebug";
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_6;
vm_args.options = options;
vm_args.nOptions = 3;
res = JNI_CreateJavaVM (&jvm, (void **)&env, &vm_args);
if(res < 0 || !env)
printf("\nUnable to Launch JVM\n");
else
printf("**JVM launched successfully**\n");
cls = ( * env) -> FindClass(env, "HiveJdbcClient2");
printf("**cls**:\n",cls);
if (cls!=NULL)
printf("**HiveJdbcClient2 find success**\n");
else
printf("**Something happend with FindClass\n");
mid = (* env) -> GetStaticMethodID (env,cls, "main", "([Ljava/lang/String;)V");
if (mid!=0)
{
printf("**GetStaticMethodID success -test worked**\n");
(* env) -> CallStaticVoidMethod (env, cls, mid, NULL);
}
(* jvm) -> DestroyJavaVM (jvm);
return 0;
}
When I run just the java program, then the jdbc connection seems to to work. However, when invoked through the C program, the execution stops at DriverManager.getConnection() without any error messages or warning. I've checked that the hiverserver2 is running. Are there any additional configurations that need to be done like in hive-site.xml? I'm using cloudera CDH virtualbox so have not performed any additional configurations. I've seen a couple of similar queries and resolutions on this site but nothing has worked for me....
I resolved the issue by catching exceptions in the JNI calls. By way, the issue was missing classes which I corrected in -D.Java.path.
I am using Oracle Express Edition to work with a project. I have the following code to connect to my database:
public static void main(String[] args) {
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con = DriverManager.getConnection("jdbc:oracle:driver_type:[username/password]#//host_name:port_number:SID");
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("Select * from [Sheet1$]");
ResultSetMetaData rsmd = rs.getMetaData();
int numberOfColumns = rsmd.getColumnCount();
while (rs.next()) {
for (int i = 1; i <= numberOfColumns; i++) {
if (i > 1)
System.out.print(", ");
String columnValue = rs.getString(i);
System.out.print(columnValue);
}
System.out.println("");
}
st.close();
con.close();
} catch (Exception ex) {
System.err.print("Exception: ");
System.err.println(ex.getMessage());
}
}
When I try to run it, I get a failed to call database error, Missing in or out parameter at index 1. What is wrong with my code? Any help would be appreciated.