mysql statement WHERE as unknown - mysql

I have this sql statement:
selectAllUsersByCriteria = connection.prepareStatement(
"SELECT * FROM Users WHERE ? = ?" );
And the follow method running the statement:
public ArrayList<User> getUsersByCriteria(String 1criteria, String 2criteria)
{
ArrayList<User> results = null;
ResultSet resultSet = null;
try
{
selectAllUsersByCriteria.setString( 1, 1criteria);
selectAllUsersByCriteria.setString( 2, 2criteria);
// executeQuery returns ResultSet containing matching entries
resultSet = selectAllUsersByCriteria.executeQuery();
results = new ArrayList< User >();
while ( resultSet.next() )
{
results.add( new User( resultSet.getString( "userName" ),
resultSet.getString( "Password" ),
resultSet.getBoolean( "AdminRights" ),
resultSet.getDouble( "Balance" )
) );
} // end while
} // end try
catch ( SQLException sqlException )
{
sqlException.printStackTrace();
} // end catch
finally
{
try
{
resultSet.close();
} // end try
catch ( SQLException sqlException )
{
sqlException.printStackTrace();
close();
} // end catch
} // end finally
return results;
}
It doesn't work. I figure it is the first ? that is the issue. Isn't it possible to set the WHERE ? as a ?. Can it be solved in another way.
It is a table I want to show, but it should only be show the users follow it meet the two criteria.

You would need to inject the column name directly into the string. That would open you up to a SQL injection attack, so I'd recommend querying (and probably caching) the table's schema info (specifically found in INFORMATION_SCHEMA.COLUMNS).
This way you can make sure that your user-submitted column name matches one of the column names in your table before injecting it into the script by seeing if it's in your list of available columns.

Related

Android Development: App crashes As soon as cuursor is called to search for a row in my table

I am quite new to android development, I am making an app in which i need to search a row in my table which has 3 columns Id, Items and User.
In this table I have to search for a particular user and return string Items of that row.
But as soon as I call cursor, the app crashes; why is that? And is there any other way to do this?
public String loadData(String user,Context context)
{
SQLiteDatabase sq = this.getReadableDatabase();
String[] Columns = {C1,C2,C3};
Cursor cr = sq.query(Table2, Columns, null, null, null, null, null);
if (cr.getCount()==0) {
Toast.makeText(context,"Something Is wrong,Not Able to Load",Toast.LENGTH_SHORT).show();
return "";
}
else {
String ans = "";
do {
if (cr.getString(2).equals(user)) {
ans = cr.getString(1);
break;
}
} while(cr.moveToNext());
return ans;
}
You need to move the cursor to a valid row before attempting to read column value with getString() from it. You can move it with e.g. moveToFirst() or moveToNext() that return true when the cursor is pointing to a valid row.
In other words, you need a if (cr.moveToFirst()) before your do-while loop.

ASP.NET MVC validate query from controller to ajax

Currently I have an insert query to my table and I don't have any idea how I can validate it, if the data is already exists in the table.
So for now I just used try-catch to handle the duplicate entry. I just wondering if I could return some text If value goes to catch then display it to my view as alert or something.
Here is my query from controller:
public ActionResult AddUser(int id, string name, string age)
{
string constr = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
using (MySqlConnection con = new MySqlConnection(constr))
{
string sqlQuery = "INSERT INTO myTable (id, name, age) VALUES (#id, #name, #age)";
MySqlCommand cmd = new MySqlCommand(sqlQuery, con);
cmd.Parameters.AddWithValue("#id", id);
cmd.Parameters.AddWithValue("#name", name);
cmd.Parameters.AddWithValue("#age", age);
con.Open();
try {
cmd.ExecuteNonQuery();
con.Close();
return RedirectToAction("Index");
}
catch (Exception )
{
con.Close();
return this.Json("This Data already exist on table");
}
}
}
And this is my script to read my controller query:
function add(id, name, age) {
var result = confirm("Are you want to add " + name + " to list?");
if (result == true) {
$.ajax({
url: '/Home/AddUser',
type: 'POST',
data: {
'id': id,
'name': name,
'age': age,
},
success: function (data) {
alert('Data has been successfully added');
},
error: function (jqXhr, textStatus, errorThrown) {
alert(errorThrown);
}
});
}
}
So far I can successfully insert it to my table but if there is already exist and it's a duplicate it will do nothing so the user will not have a prompt if they already added it or already exist or not.
Any suggestions or comments. TIA.
Consider creating a constraint in your database table (unique constraint on that column(s)). That will prevent any duplicate records to be saved even if it passes your C# code which is talking to the database.
Add a check to see whether the record exist, just before executing the INSERT statement. You can write a SQL statement which checks before the insertion step. May be a stored procedure which has this SQL scripts which does this and you may call the stored procedure from your c# method.
There is no point in returning a redirect response if you are calling this from Ajax code. Consider returning a JSON structure which can tell the client side code whether the record was successfully inserted or duplicate found or code crashed.
Here is a quick and simple example, where I am calling the UserExist method, which checks whether there is a record with the specified name. If it returns false, I continue to execute my code where I will try to insert. If you are using a stored procedure, you can add the record exist check inside that as well.
[HttpPost]
public ActionResult AddUser(int id, string name, string age)
{
try
{
// your existing code to insert record to db
// Check record exist
if(UserExist(name))
{
return Json(new { status="failed", message = "Name exist"});
}
// name does not exist. Try to call the Insert code now.
return Json(new { status="success", message = "Successfully saved"});
}
catch (SqlException exs)
{
// Check exs to see whether this was caused by unique constraint violation
return Json(new { status="error", message = "User exist"});
}
catch (Exception ex)
{
// to do : log the exception
return Json(new { status="error", message = "Error in saving"});
}
}
private bool UserExist(string name)
{
// to do: check record exist in db
// You may use ExecuteScalar method if you use raw ADO.NET
// to do : return boolean value.
}
and in your success handler, check the status property of the response json and show the user the appropriate message
success:function(data)
{
if(data.status==="success")
{
alert("Saved successfully");
}
else if(data.status==="failed")
{
alert(data.message);
}
}
You can set the status property of your JSON object to failed when you are trying to insert duplicate record.
You can use specific exceptions for your exception handling part which can catch the exception when the unique key constraint is violated in the database level. Send a message to user in that catch block as well.
The first step you can do is creating a stored procedure which has output parameters to show insertion status, which must be different when duplicate data is found:
DELIMITER //
CREATE PROCEDURE AddUser(in #id int, #name varchar(50), #age int, out #status varchar(20))
AS
BEGIN
-- check if duplicate exists
IF (SELECT EXISTS (SELECT 1 FROM myTable WHERE name = #name))
BEGIN
-- duplicate exist, no insertion to table
SET #status = 'duplicate';
END
ELSE
BEGIN
INSERT INTO myTable (id, name, age) VALUES (#id, #name, #age)
SET #status = 'success';
END
END
END
//
DELIMITER ;
Then, use the stored procedure name inside MySqlCommand and use its output parameter to return the status string:
[HttpPost]
public ActionResult AddUser(int id, string name, string age)
{
string constr = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
using (MySqlConnection con = new MySqlConnection(constr))
{
string sqlQuery = "AddUser";
MySqlCommand cmd = new MySqlCommand(sqlQuery, con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#id", id);
cmd.Parameters.AddWithValue("#name", name);
cmd.Parameters.AddWithValue("#age", age);
cmd.Parameters.Add("#status", MySqlDbType.VarChar).Direction = ParameterDirection.Output;
con.Open();
try
{
cmd.ExecuteNonQuery();
con.Close();
return Json(new { status = (string)cmd.Parameters["#status"].Value });
}
catch (MySqlException ex)
{
con.Close();
return Json(new { status = "error", message = ex.Message });
}
catch (Exception e)
{
con.Close();
return Json(new { status = "error", message = e.Message });
}
}
}
Then you can output messages depending on current status in AJAX callback:
$.ajax({
url: '/Home/AddUser',
type: 'POST',
data: {
'id': id,
'name': name,
'age': age,
},
success: function (data) {
if (data.status === "success")
{
alert("Data has been successfully added");
}
else if (data.status === "duplicate")
{
alert("This Data already exist on table");
}
else if (data.status === "error")
{
alert(data.message);
}
}
error: function (xhr, status, err) {
// error handling
}
}
If you don't want to check with SELECT query like above, consider altering the table by applying UNIQUE constraint and check against error code 1062 in MySqlException:
ALTER TABLE myTable ADD CONSTRAINT UNIQUE (name);

JavaFX MySQL database return sum

I want to use datepicker to select range of dates. This range of dates is then queried in the database where the sum is counted...
My attempt:
public static int dateRange(){
int value = 0;
PreparedStatement stmt = null;
try {
Connection connection = DriverManager.getConnection("", "", "");
stmt = connection.prepareStatement("SELECT SUM(cost) FROM Items WHERE expiration_date between '" + Budget.datePicker1.getValue() + "' and '" + Budget.datePicker2.getValue() + "'");
ResultSet result = stmt.executeQuery();
result.next();
String sum = result.getString(1);
value = Integer.parseInt(sum);
} catch (Exception e){
value += 0;
}
return value;
}
It works. It returns the total if the days are there so to speak. If there are no days in the database as selected in the DatePicker then 0 pops up... But it looks messed up (catch block) and I was wondering if anyone could help me with an alternative solution?
First, since the value returned by the query is an integer value, there is no need to read it as a string and parse it. Just use result.getInt(...).
Second, if you are going to use a PreparedStatement, use it to properly set parameters, instead of building the query out of concatenated strings. Doing the latter exposes your application to SQL injection attacks.
If none of your dates are in range, then the SQL query will return NULL. Calling getInt() on column in a result set that is NULL will return 0, so you will get the result you want in that case anyway. If the previous value you got from a result set was SQL NULL, then calling result.wasNull() will return true, so if you really did need to handle that case separately, you could use that mechanism to do so.
You can do:
public static int dateRange(){
int value = 0;
// Use try-with-resources to make sure resources are released:
try (
Connection connection = DriverManager.getConnection("", "", "");
PreparedStatement stmt = connection.prepareStatement(
"SELECT SUM(cost) FROM Items WHERE expiration_date between ? and ?");
) {
stmt.setDate(1, Date.valueOf(Budget.datePicker1.getValue()));
stmt.setDate(2, Date.valueOf(Budget.datePicker2.getValue()));
ResultSet result = stmt.executeQuery();
result.next();
// Note that if there are no values in range, the SQL result will be NULL
// and getInt() will return 0 anyway.
value = result.getInt(1);
// however, if you need to explicitly check this and do something different
// if nothing is in range, do:
if (result.wasNull()) {
// nothing was in range...
}
} catch (SQLException e){
// this actually indicates something went wrong. Handle it properly.
Logger.getGlobal().log(Level.SEVERE, "Error accessing database", e);
// inform user there was a db error, etc...
}
return value;
}

Nullable Table Valued Parameter in Stored Procedure

I have this procedure
CREATE PROCEDURE dbo.spProcedure1
#intArray as dbo.intArray READONLY
AS
BEGIN
-- ...
END
which use user type as a parameter
CREATE TYPE dbo.IntArray AS TABLE (IntValue int NULL)
and I am calling the procedure from the C# ASP.NET MVC 4 project
// creating empty SQL #IntArray parameter
var emptyIntDataTable = new DataTable();
emptyIntDataTable.Columns.Add("IntValue");
// calling stored procedure
return Database.SqlQuery<int>(
#"spProcedure1 #p1",
new SqlParameter("p1", (object)Utils.ToDataTable(m.IntArray) ?? emptyIntDataTable)
).ToList();
// ToDataTable method which is returning null
public static DataTable ToDataTable<T>(this IList<T> data)
{
if (data == null)
return null;
... // code omitted because it is not working yet
}
the error which is throwed when calling stored procedure is
The table type parameter 'p1' must have a valid type name.
How to pass an empty table value?
Passing the list instead of datatable throw following error
var emptyIntDataTable = new List<int>;
No mapping exists from object type System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] to a known managed provider native type.
In your code:
where it says:
return Database.SqlQuery<int>(
#"spProcedure1 #p1", new SqlParameter("p1",
(object)Utils.ToDataTable(m.IntArray) ?? emptyIntDataTable)
).ToList();
Change it to read:
return m.IntArray.Length > 0?
Database.SqlQuery<int>(#"spProcedure1 #p1",
new SqlParameter("p1",
(object)Utils.ToDataTable(m.IntArray))).ToList():
Database.SqlQuery<int>(#"spProcedure1")).ToList();
sample to show how to not pass table parameter
CREATE TYPE dbo.KeyIds]
AS TABLE(pkId int NOT NULL,
PRIMARY KEY CLUSTERED (pkId ASC)
WITH (IGNORE_DUP_KEY = OFF))
Go
-- ------------------------------
Create procedure testProc
#aIds dbo.keyIds readonly
as
Set NoCount On
if exists (select * from #aIds)
Select * from #aIds
else
Select 'No Aids passed in'
Go
-- ------------------------------
Exec dbo.testProc -- <--- Here I am NOT passing the #aids parameter
But, even though I am NOT passing the #aids parameter
it still works, and the subquery (select * from #aIds) still functions, and since it is an empty datatable the SP returns the empty message 'No Aids passed in'.
On the other hand, if you pass the parameter
Declare #MyIds dbo.keyIds
Insert #MyIds Values(1)
Insert #MyIds Values(2)
Insert #MyIds Values(3)
Insert #MyIds Values(4)
Insert #MyIds Values(5)
Exec dbo.testProc #MyIds -- <--- Here I AM passing the #aids parameter
it outputs the contents of the datatable parameter
C# code example...
public DataTable GetAccountTransactions(IEnumerable<int> accountIds)
{
const string procName = "FetchAccountTransactionData";
var acctIds = accountIds == null ?
new List<int>() : accountIds.ToList();
// -------------------------------------------------
var parms = DbParamList.Make();
// DbParamList is a List<IDbDataParameter>
// See here, ONLY ADD PARAMETER if list is NOT empty!
if (acctIds.Count > 0)
parms.AddSQLTableParm("aIds", acctIds);
try
{ // following constructs command obkect and calls SP
return Utilities.GetDataTable(schemaNm + "." +
procName, parms, copConn);
}
catch (SqlException dbX)
{
// Exception stuff
}
}
public class DbParamSortedList : SortedList<string,IDbDataParameter> { }
The alternative solution
prepare method for converting List<int> into dbo.IntArray type
public static DataTable IntArrayToDataTable(IEnumerable<int> ids)
{
if (ids == null)
return null;
DataTable table = new DataTable();
// datatable columns has to have same name as database type !
table.Columns.Add("IntValue", typeof(int));
foreach (int id in ids)
{
table.Rows.Add(id);
}
return table;
}
run sql stored procedure
var sqlParameters = new List<object>();
var parameter1 = Utils.IntArrayToDataTable(m.IntArray);
if (parameter1 != null)
sqlParameters.Add(new SqlParameter("intArray", parameter1)
// these variables are the key, without them it is not working
{
SqlDbType = SqlDbType.Structured,
TypeName = "dbo.IntArray"
});
else // parameter cannot be omitted !! even if all parameters are named !! otherwise parameter mismatch happens (in case of multiple parameters)
sqlParameters.Add(new SqlParameter("intArray", SqlDbType.Structured) { TypeName = "dbo.IntArray" });
var sqlQuery = "spProcedure1 #InArray";
return Database.SqlQuery<int>(sqlQuery, sqlParameters.ToArray()).ToList();

After end of result set error (probably with string)

I'm just wondering how to get rid of this error:
try {
int ppointd;
String nazwa;
int smr;
int zab;
PreparedStatement pktUpdate = Main.c.prepareStatement("SELECT * FROM `staty` ORDER BY `pkt` DESC LIMIT 10");
ResultSet rs1 = pktUpdate.executeQuery();
for(int i = 1; i<11; i++) {
rs1.next();
nazwa = rs1.getString("Nazwa");
zab = rs1.getInt("zab");
smr = rs1.getInt("smr");
ppointd = rs1.getInt("pkt");
p.sendMessage("§a"+i+". §e" + nazwa + " §b- §6PKT: §7[§4" + ppointd+ "§7] §6ZAB: §7[§4"+zab+"§7] §6SMR: §7[§4"+smr+"§7]");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
I made some digging and I know it's about this String, but I have no clue how to skip this and have same output. Any ideas?
You call rs1.next() in a loop from 1 to 10, but what if your SELECT query returns fewer than 10 rows?
When you call rs1.next() you need to test if that method returns false, and if so, break out of the loop.
For example, it's more common to code it the following way instead of using a for loop:
while (rs1.next()) {
. . .
}
That way the loop automatically stops when there are no more rows.