As a C# developer new to Java, i thought it might be easiest if i simply show a bit of C# code so i can see what the equivalent Java JDBC calls are:
String myConnectionString = "...";
String mySql = "select name from people where id = #myId";
int myId = 123456;
List<Field> fields = new List<Field>();
using (SqlConnection conn = new SqlConnection(myConnectionString)
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(mySql,conn))
{
cmd.Parameters.AddWithValue("#myId", myId);
using(SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
String name = rdr.GetString(0);
fields.Add(name);
}
}
}
}
Now i know that the using statements above will safely close the database if anything goes wrong, whereas with java it's a bit more complicated (try..finally or something). And in java, i'm not sure what exactly needs to be disposed or closed (or whatever) - the connection only? Or the statement as well?
If you could give me a leg up, that'd be great. Thanks a lot
edit: I really like the code here: (Java Exception Handling - Style)
Connection conn = MyDatabaseLayer.getConnection();
try {
... use conn ...
}
finally {
conn.close();
}
However, do i need to do any further exception handling to ensure the statement and reader and all that other stuff gets closed? Or is closing the connection enough?
Here's a rough explanation of the steps, as liberally copied from some random page:
first, you load the driver. This will be a class in your driver jar file. In many environments you get this, actually, from a datasource, this is sort of old fashioned, but probably better to see the nuts and bolts.
Class.forName("com.imaginary.sql.msql.MsqlDriver");
then you get a connection:
Connection conn = DriverManager.getConnection("jdbc:msql://www.myserver.com:1114/....", "user1", "password");
The url string tends to be different for different db vendors. Luckily we don't swap databases too often, so you only need to look it up once. Now you can finally use the damned thing.
PreparedStatement ps = conn.prepareStatement("SELECT * FROM FOO WHERE A=?", 1);
A prepared statement gets cached, so you can use it with inseted parameters. It will work with a plain SQL statement, but for that you can just use a statement. You can also just call conn.executeQuery(...) to get a resultSet, which is what you want.
ResultSet rs = ps.executeQuery();
Now you can loop through the rs, and get whatever:
while (rs.next())
{
..
}
ResultSets also have ResultSetmetadata which gives you things like the column names, # of columns (but not the total # of results, which would be too easy).
As for try catch, you need to close your statement/result set after you use them. Every time. Otherwise bad things will happen. Like leaving open resources on your db. Since your db connect method can throw errors, you rap the whole thing in a try catch, and close your statement (and connection, if you've made it here) in a finally block.
This is why people use ORM frameworks in java.
You indeed need a try-finally block here. The Java equivalent of the using keyword will be introduced in the upcoming Java 7. A Java port of your code would look like:
// Prepare.
String url = "...";
String sql = "SELECT name FROM people WHERE id = ?";
int id = 123456;
List<String> names = new ArrayList<String>();
// Declare before try.
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
// Acquire inside try.
connection = DriverManager.getConnection(url);
statement = connection.prepareStatement(sql);
statement.setInt(1, id);
resultSet = statement.executeQuery();
// Process results.
while (resultSet.next()) {
names.add(resultSet.getString("name"));
}
} finally {
// Close in reversed order in finally.
if (resultSet != null) try { resultSet.close(); } catch (SQLException logOrIgnore) {}
if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
}
When not using connection pooling, closing the Connection alone would in most cases also close the Statement and ResultSet. Although not strictly specified in the JDBC API, the average JDBC driver would implicitly do that. But this is not the normal JDBC idiom. You should really close all the resources explicitly. This makes your code safely reusable for the case that you'd like to introduce connection pooling.
See also:
Equivalent of "using" keyword in Java
JDBC tutorial
JDBC connection pooling practices
JDBC connectivity with MySQL
Related
A program written in C# Oracle client that proved to have "Connection leak" which it is not closing all database connections and so after some time it can no longer connect to the database as there are too many open connections.
I wrote the following helper function (quite expansive):
private static int tryFindConnCount(){
var connstk = new Stack<Oracle.ManagedDataAccess.Client.OracleConnection>();
try
{
for (var i = 0; i < 10000; ++i)
{
var conn = new Oracle.ManagedDataAccess.Client.OracleConnection(
myDatabaseConnection);
conn.Open();
connstk.Push(conn);
}
}
catch(Exception e)
{
foreach (var conn in connstk)
{
conn.Close();
}
}
return connstk.Count;
}
Here is the code in a test case that uses the above:
var co = tryFindConnCount();
CodeThatMayLeakConnection();
var cn = tryFindConnCount();
Assert.That(cn, Is.EqaulTo(co));
It helped me identify at least one case that have connection leak.
The problem of tryFindConnCount is that it should never be used in production. And I think there should be some way to obtain the same value much cheaper.
How can I do this in the code so I can monitor this value in production?
Trying to find places where connections where not closed is a difficult task.
If you leave the program and forget to close the connection the last sql which was executed is stored in column SQL_ID in v$session (gv$session for RAC). You can search v$session for idle/dead sessions. You can then use v$sql to find the SQL text which may tell you more about what was done last. By this you may get a hint where to search in your code.
select a.sid, a.username, a.program, a.machine, a.sql_id, b.sql_fulltext
from v$session a, v$sql b
where b.sql_id(+) = a.sql_id
and a.username is not null -- filter system processes, maybe filter more stuff
;
You can query Oracle DB on "gv$session" view to get the info that you need. With a query on this view you can cyclically monitor the DB every 10-15 minutes for a count of connections from this program.
Example query below :
select count(*)
from gv$session
where machine = 'XXXXX'
and username = 'YYYYY'
and program = 'ZZZZZ';
You only need values that uniquely identify those connections like for example machine from which the connections originate.
Also the query is very light and doesn't add performance overhead.
I have a page load method that loads asp dropdowns with sql queries to my SQL Server 2012 database. I'm new to this and basically independently learned a lot of needed to be done for the co op project I'm working on.
I've been running into problems with connections not being closed properly and having my connection pool blow up with only moderate usage of my app so I've been trying to improve how I'm executing my queries in my c# code behind. But I am not confident in my understanding of this so I'm going to post an example of my code and hope that someone much more fluent might be able to guide me a bit.
string constr = ConfigurationManager.ConnectionStrings["CurrencyDb"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr)) {
using (SqlCommand cmd = new SqlCommand("SELECT * FROM dbo.Category")) {
try {
cmd.CommandType = CommandType.Text;
cmd.Connection = con;
con.Open();
//Populate Category Dropdown
DDCategory.DataSource = cmd.ExecuteReader();
DDCategory.DataTextField = "CategoryName";
DDCategory.DataValueField = "CategoryId";
DDCategory.DataBind();
}
catch (SqlException sqlex) {
throw new Exception("SQL Exception loading data from database. " + sqlex.Message);
}
catch (Exception ex) {
throw new Exception("Error loading Category data from database. " + ex.Message);
}
}
using (SqlCommand cmd = new SqlCommand("SELECT * FROM dbo.SubCategory ORDER BY SubCategoryName")) {
try {
cmd.CommandType = CommandType.Text;
cmd.Connection = con;
//Populate SubCategory Dropdown
DDSubCategory.DataSource = cmd.ExecuteReader();
DDSubCategory.DataTextField = "SubCategoryName";
DDSubCategory.DataValueField = "SubCategoryId";
DDSubCategory.DataBind();
}
catch (SqlException sqlex) {
throw new Exception("SQL Exception loading data from database. " + sqlex.Message);
}
catch (Exception ex) {
throw new Exception("Error loading Subcategory data from database. " + ex.Message);
}
}
}
The above are two queries of about 8 on my page load method.
My latest error was
There is already an open DataReader associated with this Command which must be closed first.
This prompted me to ask the question, I set MultipleActiveResultSets=true in my Web.config connection string and my app works now, but I feel as though that's a patch to cover what is likely crappy code.
What is the best practice for doing this? Thanks so much in advance!
Well, there could be multiple way but I feel you should wrap all this queries in a stored procedure and call that SP in your code behind. Instead of using DataReader use a DataSet and fill it up with the different resultset.
You can also use NextResult() method of datareader instance to get the next SELECT result and do your processing.
To your immediate question, you are using two cmds per one connection.
You could be better off using one-trip, one command, with a multiple-result.
This will allow you to properly .Close() your datareader and your connection...."returning them back to the pool"
Since you are using SqlServer, it supports multiple resultsets in one "trip".
SELECT * FROM dbo.Category;SELECT * FROM dbo.SubCategory
Use that as your select statement.
Use an .ExecuteReader() method.
And use a .NextResult() to move from one result (category), to the next one (subcategory)
You can see a more complete sample here:
https://msdn.microsoft.com/en-us/library/system.data.idatareader.nextresult(v=vs.110).aspx
Or even with Entity Framework here:
https://msdn.microsoft.com/en-us/library/jj691402%28v=vs.113%29.aspx?f=255&MSPPError=-2147217396
Now some extra stuff.
You are mixing your datalayer and your presentation layer.
You should have your datalayer populate "Dto" sometimes referred to as "Poco" objects, and return those to the Presentation layer.
public class Category
public string CategoryKey{get;set;}
public string CategoryName{get;set;}
public ICollection<SubCategory> SubCategories {get;set;}
..
public class SubCategory
public string SubCategoryKey{get;set;}
public string SubCategoryName{get;set;}
..
public class MyDataLayerObject
public ICollection<Category> GetAllCategories()
{
ICollection<Category> categories;
ICollection<SubCategory> subcats;
// write a datareader call here, and use it to populate multiple Category and SubCategory objects
// make sure you close the datareader when done
//now "match up" the subcats to its parent category
}
Then have your presentation layer "bind" to the ICollection of Categories.
You might have a business layer between the presentation and datalayers, but at least to the presentation and datalayers.
I also answered a question at the link below...that is similar to yours. Their objects are "Question" and "Answer" where a Question has 0:N number of Answers.
Find my answer at the below question.
Return objects with populated list properties from stored procedure
Note, you can just use the 2 select queries in a string (the second line of my/this answer), aka, you don't have to create a stored procedure.
Another option is to use nuget to get Microsoft.EnterpriseLibrary.Data.
This encapsulates alot of great practices for you...including closing connections.
You'll still have to close your datareader, but the code is alot cleaner.
These two should be in different methods. One to get category and one to get sub-categories. Then each will have a different connection and you won't have this problem.
You should call Dispose on your DataReader or either put it inside a using statement so that Dispose() is called for you.
The typical IDataReader pattern looks like this:
using (IDataReader r = query.ExecuteReader())
{
while (r.Read())
{
// etc.
}
}
When you don't call Dispose() on the IDataReader he can't be cleaned up. And I expect that your connection also can't be cleaned up even when you call Dispose() on the connection, because it's stil used by your IDataReader.
And yes per Connection you can only use one IDataReader.
So as others wrote you should split this up into more than one method and use one connection per method.
Btw.:
It would be better not to use "SELECT *". Just query the fields you need.
I'm a beginner.
I already found a way to connect to SQL SERVER using the codes below:
private void getListBtn_Click(object sender, RoutedEventArgs e)
{
SqlConnection con = new SqlConnection("Data Source=.;Initial Catalog=myDB;Integrated Security=true;");
SqlDataAdapter sda = new SqlDataAdapter("SELECT ID,Date,Name,City FROM Info;", con);
DataTable dt = new DataTable();
sda.Fill(dt);
dataGridForm.ItemsSource = dt.DefaultView;
I also wanted to get number of rows from a TABLE and set it to a label, But it's not a good idea to copy and paste this code again, I want to have a method for sqlconnection so i won't rewrite this code again and again for every single query.
Sorry i'm an absolute beginner, 3 days since i started learning C# WPF.
Yes some frameworks and/or ADO's solutions are good and maybe the best "professionnal" approch, you say you're a beginner and I was it not so far ;-).
So the simpliest way is to add a new class for the sql connection. In example add a Sqlconnect.cs class.
using System.Data.SqlClient;
public class Sqlconnect
{
public SqlConnection Con { get; set; }//the object
private string conString { get; set; }//the string to store your connection parameters
}
This class will have a method to open the connection and one to close it.
public void conOpen()
{
conString = "Data Source=..."; //the same as you post in your post
Con = new SqlConnection(conString);//
try
{
Con.Open();//try to open the connection
}
catch (Exception ex)
{
//you do stuff if the connection can't open, returning a massagebox with the error, write the error in a log.txt file...
}
}
public void conClose()
{
Con.Close();//close the connection
}
In your other(s) classe(s) where you need a sql query you first instantiate an new object.
private void getListBtn_Click(object sender, RoutedEventArg e)
{
Sqlconnect con = new Sqlconnect();//instantiate a new object 'Con' from the class Sqlconnect.cs
con.conOpen();//method to open the connection.
//you should test if the connection is open or not
if(con!= null && con.State == ConnectionState.Open)//youtest if the object exist and if his state is open
{
//make your query
SqlDataAdapter sda = new SqlDataAdapter("your query", con);
//etc
con.conClose();//close your connection
}
else
{
//the connection failed so do some stuff, messagebox...as you want
return;//close the event
}
}
this example need some ameliorations, it's evident but I wrote it like this to be clearest.
First thing this is not related to WPF, this is general coding even I would not consider this to be related to .net.
For your current problem to show the count, you dont have to make a call again. You can get the count from the datatable row count. But, I would suggest few things:
You should have one or different separate layers like business, data access etc. as per your needs.
You should not give the connection as the way you have provided here.
You can choose to use any ORMs like entity framework, NHibernate etc based on your needs. This just a direction, you can choose to stick with ADO.Net as you have it your choice. But I would definitely suggest to throw in more layers to avoid duplicate codes and more structured approach.
Best choice if you don't need so much performance is ORM like Entity Framework.
Here is something of basics.
Just use it like in MVC app.
If we copy paste your code, then the error is appearing. I have corrected it and maybe others don't need to struggle like me to find this. :)
// Object exists and State is open
if (Conex != null && Conex.Con.State ==
System.Data.ConnectionState.Open)
{
// Create a String to hold the query
string query = "insert into Xray_Table values
(25,'zzz','hij',3,'uuu',6,'2012-06-18
10:34:09.000')";
// Create a SqlCommand object and pass the constructor the connection string and the query string
SqlCommand queryCommand = new SqlCommand(query, Conex.Con);
// Execute the query to update to the database
queryCommand.ExecuteNonQuery();
// method to close the connection.
Conex.conClose();
}
I have written following code.
I have opened the database connection for once for one query
I want to execute another query.
I have written the code below.
But i think there is a mistake
Can anyone help me please?
public void check()
{
try
{
OdbcConnection myOdbcConnection = new OdbcConnection(con1);
OdbcCommand myOdbcCommand = myOdbcConnection.CreateCommand();
String sSQL = "SELECT * FROM(select tdate from tbl_IThelpdesk order by call_no desc)where ROWNUM = 1"; //last record of the call_no column
myOdbcCommand.CommandText = sSQL;
myOdbcConnection.Open();
OdbcDataReader myOdbcDataReader = myOdbcCommand.ExecuteReader();
if (!myOdbcDataReader.Read())
{
txtDate.Text = DateTime.Now.ToShortDateString();
string strcallno = DateTime.Now.Year.ToString("d2") + DateTime.Now.Month.ToString("d2") + DateTime.Now.Day.ToString("d2");
txtcall.Text = "ITHD" + strcallno + "001";
myOdbcConnection.Close();
myOdbcDataReader.Close();
}
else
{
DateTime today = DateTime.Parse(DateTime.Now.ToShortDateString());
if (myOdbcDataReader[0].ToString() == today.ToString())
{
myOdbcConnection.Close();
myOdbcDataReader.Close();
myOdbcConnection.Open();
OdbcCommand myOdbcCommand1 = myOdbcConnection.CreateCommand();
String SQLmax = "SELECT max(call_no) FROM TBL_IThelpdesk";
myOdbcCommand1.CommandText = SQLmax;
OdbcDataReader myOdbcDataReader1 = myOdbcCommand1.ExecuteReader();
while (myOdbcDataReader1.Read() != false)
{
txtcall.Text = myOdbcDataReader1[0].ToString().Trim();
}
myOdbcDataReader1.Close();
myOdbcDataReader.Close();
myOdbcConnection.Close();
}
}
}
catch (Exception e)
{
lblEmpty.Text = e.Message;
lblEmpty.Visible = true;
}
}
Since database connections use a pool, you don't have to maintain the same connection for multiple queries; instead, open a connection when you need it, and close it as soon as possible to free up the resources.
See: http://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.80).aspx
See also: C# SQLConnection pooling
Note that you've not used using() { } pattern. Given that OdbcConnection and similar types implement IDisposable, you should embed them into a using in order for them to be disposed without waiting the garbage collector.
See: http://msdn.microsoft.com/en-us/library/yh598w02.aspx
You're hitting the database twice when you only need to hit it once. You're getting the latest date then going to the database again to get the corresponding call_no. This is unsafe as the max(call_no) could change even in the small amount of time between the 2 queries.
//get the latest [call_no] and [tdate]. No need for a 2cd trip with max(call_no)
SELECT * FROM(select call_no, tdate from tbl_IThelpdesk order by call_no desc)where ROWNUM = 1
Also the data access code is mixed with UI code. You should create data access methods that do 1 thing; return the data you want. This will make it much easier to follow the main flow of your algorithm.
I have an application that fires a mysql command (query) "show databases", the query works and returns properly but I can't close my connections. The user I used had 24 connections allowed at the same time so the problem popped up further down my program but reducing the allowed connections to 2 shows me that I can't even close the first query (which isn't in a loop). The code is the following:
protected override Dictionary<string, Jerow_class_generator.Database> loadDatabases()
{
MySqlConnection sqlCon = new MySqlConnection(this.ConnectionString);
sqlCon.Open();
MySqlCommand sqlCom = new MySqlCommand();
sqlCom.Connection = sqlCon;
sqlCom.CommandType = CommandType.Text;
sqlCom.CommandText = "show databases;";
MySqlDataReader sqlDR;
sqlDR = sqlCom.ExecuteReader();
Dictionary<string, Jerow_class_generator.Database> databases = new Dictionary<string, Jerow_class_generator.Database>();
string[] systemDatabases = new string[] { "information_schema", "mysql" };
while (sqlDR.Read())
{
string dbName = sqlDR.GetString(0);
if (!systemDatabases.Contains(dbName))
{
databases.Add(sqlDR.GetString(0), new MySQL.Database(dbName, this));
}
}
sqlCom.Dispose();
sqlDR.Close();
sqlCon.Close();
sqlCon.Dispose();
return databases;
}
P.S. The 'New MySQL.Database(dbName, this));' is my owm made class which only stores the DB structure, could be considered irrelevant.
The exact error I get is 'max_user_connections'. on the connection.open line of the next time a query needs to be fired.
Rather than keeping track of all the Open/Close/Dispose calls all over the place, I'd recommend just replacing all of those with using statements. This will make sure the expected scope of each object is clear and that it will be destroyed/disposed upon exiting that scope.
Close() nor using will help alone with your problem because ADO.NET is using its own connection pooling and connections are by default not closed until program is closed. There are few options to solve this, but consider performance implications and is this really desired behavior for your application.
Add ";Pooling=False" to your connection string.
SqlConnection.ClearPool Method
SqlConnection.ClearAllPools Method
For more information read: SQL Server Connection Pooling (ADO.NET)
Along with the using suggestions above, when creating your sqlDR variable you should use the CloseConnection command behavior to close the actual connection if that is your intended action. As noted in the documentation here.
When the command is executed, the associated Connection object is closed when the associated DataReader object is closed.
So your code to instantiate your reader would look like this:
//to instantiate your variable
MySqlDataReader sqlDR;
sqlDR = sqlCom.ExecuteReader(CommandBehavior.CloseConnection);
//closing your datareader reference here will close the connection as well
sqlDR.Close();
If you wrap all your code in a using block using the above method, you don't need any of those Close() or Dispose() methods other than the sqlDR.Close();
when use "using" key word what happen is.when the garbage collector activate it first dispose objects which was declred in using statement.
I recommend using connection pooling in combination with the MySqlHelper class, passing the connection string as the first argument. That allows MySQL to open the connection if necessary, or keep it open according to the pooling cfg, without you having to know about it.
I changed my code to use 1 connection and keep it open and when testing I came across an error that a datareader should be closed. Now since all my queries didn't close the dataReader object (I used dataTable.Load(cmd.ExecuteReader()).) I think the problem might be there.
Keeping 1 open connection worked perfectly so I don't know what caused the not closing problem. I gues it was the dataReader not closing by itself.
Close() will definitely help you close your.
using (MySqlConnection conn = GetConnection())
{
conn.Open();
using (MySqlCommand cmd = conn.CreateCommand())
{
if (conn.State != ConnectionState.Open)
{
conn.Open();
}
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "UserDetail";
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
list.Add(new Album()
{
Id = Convert.ToInt32(reader["UId"]),
Name = reader["FirstName"].ToString(),
ArtistName = reader["LastName"].ToString()
});
}
}
}
}
In the above code, you can see one if condition before opening the connection it will help you to reuse your already open connections check below code.
if (conn.State != ConnectionState.Open)
{
conn.Open();
}