I'm trying to implement a program (in Java) that check if two values exists in a database (PostgreSQL). This is my problem:
I have an external document that Java reads this values:
# letter_id, child_id, toy_id
18,1,2
18,1,3
18,2,1
The first row put into a database this values for this reason: letter doesn't exists in my database and then, insert letter_id and child_id into LETTER (it's a table) and insert WISHED TOY (it's a table).
The second row is inserted into the database for this reason: letter exists and child_id is the same, but toy_id is different. Then, update values to LETTER and insert letter_id and toy_id into WISHED TOY.
The third row will fail, for this reasons: letter exists and child_id is different and this is not possible, only one child can write 1 letter. It is not possible to add this case because two children cannot write the same letter.
I have this functions that I want use:
private Integer getLetterId(List<String> row)
{
String integer;
String[] rowArray = (String[]) row.toArray(new String[0]);
integer = rowArray[0]; //Letter_Id
return (null != integer) ? Integer.valueOf(integer) : null;
}
private Integer getChildId(List<String> row)
{
String integer;
String[] rowArray = (String[]) row.toArray(new String[0]);
integer = rowArray[1]; //Child_Id
return (null != integer) ? Integer.valueOf(integer) : null;
}
My idea is this:
If letter exists {
If letter exists for this child {
UPDATE LETTER
INSERT WISHED
}
ELSE {
//this case will be return an error because letter not coincide with child
}
Else {
Insert Letter
Insert Wished
}
}
Wished is an another table that puts letter and toy_id in case that exists or not letter.
I understand how to implement the first 'if': (if (rs.next()))
But I think I have problems with this 'if': if (childId==row.getChildId(1))
So, how can I do it? Any help please?
EDIT: Sorry, I don't know what I'm doing bad:
if (conn != null) {
PreparedStatement selectPS = null;
ResultSet rs = null;
Integer childId;
Integer letterId;
try {
String selectSql = "SELECT child_id FROM letter WHERE letter_id = ?";
selectPS = conn.prepareStatement(selectSql);
for (List<String> row : fileContents) {
// TODO Update or insert record from PLAYER for every row in file
selectPS.clearParameters();
setSelectPreparedStatement (selectPS,row);
rs = selectPS.executeQuery();
childId = rs.getInt("child_id");
letterId = rs.getInt("letter_id");
if (rs.next())
{
if (childId == getChildId (row))
{
System.out.println("Updating");
}
if (childId != getChildId (row) && letterId == getLetterId(row))
{
System.out.println("This letter are used by other child");
}
}
else
{
System.out.println("Inserting values to letter and wished");
}
}
}
}
I obtain this error:
ERROR: Executing
SQL ResultSet not positioned properly, perhaps you need to call next.
After rs = selectPS.executeQuery(); you have to check if there is any row at all, it means rs.next() should be run. I mean you have to move
childId = rs.getInt("child_id");
letterId = rs.getInt("letter_id");
after the if(rs.next()){
Related
I am looking at this post How to insert array values in Mysql database using java with keen interest. But just to get started I find that MySQL Workbench rejects the indicated sql statements for table creation and update(insert).
I ended up loading a single line dataframe from R into my MySQL demo database to create table itemtable. This is not ideal since the item values are intended to be integers, but loading like this from R creates the fields as double.
itemtable fields are "time", "item1", "item2", "duration"
initial line values are 0.0, 1, 1, 0.0
I created an Item class:
public class Item {
String name;
double value;
public Item(String name, double value) {
this.name = name;
this.value = value;
}
In my real application Items are far more complex and value is calculated at different times. So I use a LinkedList to hold these. The quantity of items is variable from model to model as an input of other data tables just as I did from R.
The full test code - That works!!! as follows:
public class Item {
String name;
double value;
public Item(String name, double value) {
this.name = name;
this.value = value;
}
}
import java.sql.*;
import java.util.LinkedList;
public class ItemTest {
public static void main(String[] args) throws SQLException {
Connection conn = null;
PreparedStatement pstmt = null;
Statement stmt = null;
ResultSet rs = null;
String dbUrl = "jdbc:mysql://localhost:3306/demo?useSSL=false";
String user = "student";
String pass = "student";
LinkedList<Item> itemList = new LinkedList<>();
itemList.add(new Item("item1", 0.0));
itemList.add(new Item("item2", 1.0));
double timeNow = 30.0;
double duration = 0.0;
String qMarks = "";
String names = "";
for(int i = 0; i < itemList.size(); i++) {
qMarks = qMarks + "?,";
names = names + itemList.get(i).name + ",";
}
System.out.println(qMarks);
System.out.println(names);
String pquery = "insert into itemtable(time," + names +
"duration) values(?," + qMarks + "?)";
System.out.println(pquery);
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo", "student" , "student");
pstmt = conn.prepareStatement(pquery);
pstmt.setDouble(1, timeNow);
for(int i = 0; i<itemList.size(); i++) {
pstmt.setDouble(i+2, itemList.get(i).value);
}
pstmt.setDouble(itemList.size()+2, duration);
int count = pstmt.executeUpdate();
System.out.println(count + " inserted");
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from itemtable");
while (rs.next()) {
System.out.println(rs.getDouble("time") + ", " + rs.getDouble("item1"));
}
}
catch (SQLException exc){
}
finally {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
}
}
}
Full verification of the table insert is confirmed in MySQL Workbench.
It would still be nice if I could create the itemtable from within java code.
As the linked SO post tells you: SQL's 'ARRAY' concept (which is mirrored by JDBC's .setArray and .getArray and java.sql.Array) just doesn't work in mysql. At all.
I suggest you use a better DB engine, such as postgres. Otherwise, well, you can't have arrays; what you can try to do is make a table to represent it. The usual many-to-one stuff in DBs: Instead of 'an array', you have a second table containing 1 row for each 'slot' in all arrays across the entire dataset: Each row in this 'array table' contains the data as well as a reference.
So, for example:
-- This works in PSQL:
CREATE TABLE example (id integer, whatever text, names text[], PRIMARY KEY(id));
-- but the above does not work in MySQL, so there do e.g.:
CREATE TABLE example (id integer, whatever text, PRIMARY KEY(id));
CREATE TABLE example_names (id integer, example_id integer, name text, PRIMARY KEY(id));
CREATE INDEX example_names_lnk ON example_names(example_id);
It turns out that what I was trying to do with the list item with quantity defined at runtime was indeed different than trying to enter an array in a MySQL table, which I now understand cannot be done. I do appreciate the earlier discussion, which sparked my interest in solution I needed.
The create table sql is generated in a straight forward way, the key to handling the unknown content at runtime was to build the query string using a loop for the item columns. A single loop built internal string items for the create statement as well as the prepared statements.
So the sql for creating the table in java code is straight forward. Item values can now be integers as intended.
String qMarks = "";
String names = "";
String items = "";
for(int i = 0; i < itemList.size(); i++) {
qMarks = qMarks + "?,";
names = names + itemList.get(i).name + ",";
items = items + itemList.get(i).name +" INTEGER,";
}
String createsql = "CREATE TABLE ITEMTABLE (" +
"time DOUBLE, " + items +
"duration DOUBLE)";
I am facing troubles when building a generic preparedStatement : I have 8 SQL Tables, which are all manipulated the same way, so I'd like to build a unique manager which could insert into / select from any of the 8 tables.
To do so, each table has a descriptor, which can provide the fields of a table, its name and the array of values when inserting.
In the manager, the prepared statement to insert is of the following form :
"INSERT INTO " + table_name + " VALUES (?)"
Then, I fill the gap with something like
myPreparedStatement.setString(1, values.getAllValues());
the getAllValues() method must return a string which holds every fields, like " 'This', 'Is', 3, 'example' ".
I have no problem with strings and numbers, but I can't add any date in those values...
Using September 3rd, 2008 as example, I used the following formats :
2008-09-03,
08-09-03,
080903,
03092018, but all fail. "yyMMdd" format seemed like the best option from what I saw here and there, but I have the error :
"java.sql.SQLDataException: ORA-01843: not a valid month"
And I have no idea why... has anyone faced this issue before ?
I know there are lots of posts here that talks about inserting dates in database, but they all use the
preparedStatement.setDate(pos, Date);
Statement, and I can't do that since the dates aren't in the same position in all of my tables.
EDIT :
As asked in the comment, here is a minimal sample that reproduce what I'm trying to do. If you want to reproduce as well, I let you handle the connection and database setup :
public class Sample {
public void saveAll() throws ServiceException {
Connection c = null;
PreparedStatement ps = null;
String sql = "INSERT INTO " + getTableName() +" VALUES (?)";
try {
c = getConnection();
c.setAutoCommit(false);
batch = c.prepareStatement(sql);
batch.setString(getAllFieldValues());
int res = batch.executeUpdate();
c.commit();
} catch (BatchUpdateException b) {
throw new ServiceException("Erreur lors de l'exécution du batch", b);
} catch (SQLException s) {
throw new ServiceException("Impossible de sauvegarder les beans en base.", s);
} finally {
getManager().close(batch);
freeConnection(c);
}
}
public String getAllFieldValues() {
return "'Hello', 'World', 42, '171228'";
}
public String getTableName() {
return "myTableName";
}
}
There is no such thing as generic preparedStatement in JDBC. To insert four columns in table T you must use
INSERT into T (col1,col2,col3,col4) values (?,?,?,?)
You may ommit the first list with the column names, but this is a bad practice as you trust on the actual columns of the table that may change.
Using only
INSERT into T values (?,?,?,?)
work fine until somebody modifies the table by adding or dropping a column and will fail afterwards.
All bind variables must be set extra with the setXXX method with appropriate type and index of the column starting with 1.
stmt.setInt(1,100)
stmt.setString(2,'xxx')
If I can understand your question correctly. For dynamically placing your date value in prepared statement you can override setString() method to have your custom code to check for date value or else.
Or rather in case you can also have local method to check if coming string is of format date.
For this you can simply pass Date String with some prefix attached so that you can check it in custom setString() method.
setString(String string, int position){
if(string.contains("SPECIFIC_PREFIX_CONSTANT")){
//batch.setDate(position, string.substring("REMOVE PREFIX AND ATTACH"));
}else{
//batch.setString(position, string);
}
}
Ok guys, I managed to have my stuff working, big thanks to all of you !
In case somebody else would end on my question, I'll recap the Code I have now, which works :)
So, as said previously, we have one Manager that interacts with the database and which has no knowledge of the table's he interacts with.
Here is the code of the save method of this manager :
public void saveAll(AbstractBeanClass[] values, String refSelected) {
// connexion setup
Connection c = null;
PreparedStatement batch = null;
// fetch table's fields, to prepare the placeholders
String fields = values[0].getAllFields();
String sql = "INSERT INTO " + values[0].getTableName() + " (" + fields + ") VALUES (";
StringBuffer places = new StringBuffer();
int [] res = null;
// Start at 1 to have one field left, to end the parenthesis
for(int i = 1; i < values[0].getNumberOfFields(); i++) {
places.append("?, ");
}
// last field
places.append("?)");
sql = sql.concat(places.toString()); // We now have a full (?, ..., ?)
try {
c = getConnection();
c.setAutoCommit(false);
batch = c.prepareStatement(sql);
// Filling the batch
int j = 1;
for(AbstractBeanClass bean : values) {
int i = 1;
for(String type : bean.getAllTypes()) {
switch(type) {
case "int" : {
batch.setInt(i, (int) bean.getOrderedValue(i));
}
break;
case "String" : {
batch.setString(i, (String)bean.getOrderedValue(i));
}
break;
case "Date" : {
batch.setDate(i, (java.sql.Date) bean.getOrderedValue(i));
}
break;
}
i++;
}
batch.addBatch();
// In case of numerous insertions, some Databases don't allow more than 1000 inserts at a time
if(j%1000 == 0) {
res = batch.executeBatch();
for(int k : res) {
if(k == Statement.EXECUTE_FAILED) {
getManager().close(batch);
freeConnection(c);
throw new RuntimeException("Error while inserting values.");
}
}
}
j++;
}
// last execution
res = batch.executeBatch();
for(int i : res) {
if(i == Statement.EXECUTE_FAILED) {
getManager().close(batch);
freeConnection(c);
throw new RuntimeException("Error while inserting values in database.");
}
}
c.commit();
logger.debug("Insertion succeeded, we inserted " + j + " lines.");
} catch (BatchUpdateException b) {
throw new RuntimeException("Error in batch : ", b);
} catch (SQLException s) {
throw new RuntimeException("Error : we couldn't save the values : ", s);
} finally {
getManager().close(batch);
freeConnection(c);
}
}
So this is the main part of the program, but it needs the table descriptor. To keep it simple, I made an abstract class which declares the methods I need, and all table descriptors extends this class, here is the declaration :
package com.fr.sncf.fret.boctarification.domaine.referentiel;
import java.io.Serializable;
import java.text.SimpleDateFormat;
public abstract class DaoGenericReferentielBean implements Serializable {
private static final long serialVersionUID = 1L;
protected String allFields;
// the date Format used to insert the dates in base
protected final SimpleDateFormat format = new SimpleDateFormat("yy-MM-dd");
public DaoGenericReferentielBean() {
// empty constructor
}
/**
* Return all columns' names, ordered according to database's order
* #return
*/
public String getAllFields() {
return this.allFields;
}
/**
* Returns all values ordered by columns' order
* #return String
*/
public abstract String getAllFieldsValues();
/**
* #return the table name
*/
public abstract String getTableName();
/**
* #return the number of field in this table
*/
public abstract int getNumberOfFields();
/**
* Returns the ordered list of column's type
*/
public abstract String[] getAllTypes();
/**
* Return the value corresponding to the given index
* Values are treated here according to the database's columns order
* #param index the column's number
* #return an Object, either an int, or a String, or a Date
*/
public abstract Object getOrderedValue(int index);
}
All you need now is to describe your table according to this model, Hope it helps !
I have a problem in validating my delete query anything I type even if the data is not on my database it keeps deleting it says success I want it to have an error if the user type a data that is not exists in the database. Here's my code:
try{
System.out.println("Enter record you want to delete: ");
frail = scn.nextLine();
}catch(Exception ee){
System.out.println(ee.getMessage());
}
try{
stmt = conn.createStatement();
String sqlII = "delete from tbl_test where test_name = ?";
PreparedStatement psII = conn.prepareStatement(sqlII);
psII.setString(1, frail);
psII.executeUpdate();
int rowAffacted = psII.executeUpdate();
if (rowAffacted != 0) {
System.out.println("Deleted!");
}else{
System.out.println("No Affected Rows!");
}
}
catch(Exception eer){
System.out.println(eer.getMessage());
}
psII.executeUpdate(); returns an int. If these value is zero, no lines are delete, so you can see that the user exists is not in the database and you can show an error. Is the user is corect, the value should be grater than zero.
int noOfAffectedRows =psII.executeUpdate();
if (noOfAffectedRows = 0){
//show Error
}
You could catch the return value of executeUpdate as below:
int rowAffacted = psII.executeUpdate();
if (rowAffacted != 0) {
System.out.println("Deleted!");
}
Javadoc for executeUpdate's return value says
either (1) the row count for SQL Data Manipulation Language (DML) statements or (2) 0 for SQL statements that return nothing
executeUpdate() returns the number of rows altered or returns 0 in case no rows are affected, so in your case you can do something like:
int alteredRows=psII.executeUpdate();
if(alteredRows==0)
{
System.out.println("No rows deleted");
}
else
{
System.out.println(alteredRows +"rows deleted");
}
I have created a custom function to insert data in my MySQL database. The functions first creates a query based on the input given. The query wil look like INSERT INTO tableName (columnName1, ..., columnNamei) VALUES (?, ..., ?), ..., (?, ...,?). After that, the PreparedStatement needs to made, which contains the real values. These need to be added to a batch, because I want to add multiple rows at once (as showed here: Java: Insert multiple rows into MySQL with PreparedStatement). Here is the code:
insertData() Function
public static void insertData(String table, List<HashMap<String, Object>> list) throws SQLException {
//Create query: make sure all of the rows in the table get the same amount of values passed
//Prepare colnames string
String colNamesParsed = "";
int counter = 1;
//Iterate over only the first hashmap of the list (THATS WHY ALL THE ROWS NEED TO HAVE THE SAME AMOUNT OF VALUES PASSED)
for (String colName : list.get(0).keySet()) {
//Check if it is the last col name
if (counter != list.get(0).keySet().size()) {
colNamesParsed = colNamesParsed + colName+", ";
}
else {
colNamesParsed = colNamesParsed + colName;
}
counter++;
}
//Now create the place holder for the query variables
String queryVariablesPlaceholder = "";
int rowSize = 0;
for (HashMap<String, Object> row : list) {
//This part is to check if all row sizes are equal
if (rowSize == 0) {
rowSize = row.values().size();
}
else {
//Check if the rowsize is equal for all rows
if (row.values().size() != rowSize) {
System.out.println("The rows of the arrays are from a different size");
return;
}
}
String queryVariablesRow = "(?, ";
for (int j = 1; j < (row.values().size()-1); j++) {
queryVariablesRow = queryVariablesRow+"?, ";
}
queryVariablesRow = queryVariablesRow+"?)";
//Make sure the query does not start with a comma
if (queryVariablesPlaceholder.equals("")) {
queryVariablesPlaceholder = queryVariablesRow;
}
else {
queryVariablesPlaceholder = queryVariablesPlaceholder+", "+queryVariablesRow;
}
}
//The MySQL query needs to be built now
String query = "INSERT INTO "+table+" ("+colNamesParsed+") VALUES "+queryVariablesPlaceholder+";";
System.out.println(query);
//Init prepared statement
PreparedStatement statement = con.prepareStatement(query);
for (HashMap<String, Object> map : list) {
int varCounter = 1;
//Iterate over all values that need to be inserted
for (Object object : map.values()) {
if (object instanceof Integer) {
statement.setInt(varCounter, Integer.parseInt(object.toString()));
}
else if (object instanceof String) {
statement.setString(varCounter, object.toString());
}
else if (object instanceof Timestamp) {
statement.setTimestamp(varCounter, parseStringToTimestamp(object.toString()));
}
else if (object instanceof Double) {
statement.setDouble(varCounter, Double.parseDouble(object.toString()));
}
System.out.println(varCounter);
varCounter++;
}
//Add row to the batch
try {
statement.addBatch();
}
catch (SQLException e) {
e.printStackTrace();
}
}
//Execute the query, which is in fact the batch
statement.executeBatch();
}
When I want to insert some data in the database, I execute the following code:
Functional part
List<HashMap<String, Object>> list = new ArrayList<>();
for (Object object : listOfObjects) {
HashMap<String, Object> map = new HashMap<>();
map.put("columnName1", object.getSomeValue());
/....../
map.put("columnName2", object.getSomeOtherValue());
list.add(map);
}
Functions.insertData("tableName", list);
Creating the dynamic query seems to work perfectly. However, I can't get the statement.addBatch() to work. It keeps giving me the following error:
java.sql.SQLException: No value specified for parameter 9
I don't get it, because I only have 8 parameters to pass in every unit of the batch. My target table has 9 columns, so I tried to add a value for that column, but then it says: No value specified for parameter 10, so it seems like it isn't closing the 'batch unit' or something.
What am I missing here?
Any help is greatly appreciated!
This
INSERT INTO tableName (columnName1, ..., columnNamei) VALUES (?, ..., ?), ..., (?, ...,?)
is not standard SQL syntax.
If you use this JDBC will a parameter for each "?" in your query.
Use:
INSERT INTO tableName (columnName1, ..., columnNamei) VALUES (?, ..., ?)
and add every statement to a batch.
What is the best way to handle Java ResultSet? I'm creating a Desktop application that will connect to an oracle database using JDBC.
However, I am having problem with handling ResultSets since I can't do comparison using for loop.
// create a database connection object
DB db = new DB();
// get rows of my first table and second table
ResultSet firstRS = db.getMyFirstTable();
ResultSet seconRS = db.getSecondTable();
// compare the two tables
while(firstRS.next())
{
// get the row of my first table
String firstRSRow = firstRS.getString("col1");
while(secondRS.next())
{
// get the row of my second table
String secondRSRow = seconRS.getString("col1");
// compare row from first table to the row in second table
if(firstRSRow.startsWith(secondRSRow))
{
// do some processing here....
} // end if
} // end while
} // end while
I know that this could be accomplish with Hibernate with a few lines of code but I don't have the luxury of time to study it.
I also found commons DbUtils from apache but it seems complicated for me as a novice programmer.
Is there other way/tools/libraries/framework simple enough to get ResultSet from databases and manipulate it in simple and straightforward manner?
I will also appreciate if you could direct me to a website that has sample codes concerning java database connectivity.
Thank you very much for your support.
Why ResultSet? You could easily compare values with SELECT statement. In case you still want to ResultSet then think about CachedRowSet.
DB db = new DB();
// get rows of my first table and second table
ResultSet firstRS = db.getMyFirstTable();
ResultSet seconRS = db.getSecondTable();
// compare the two tables
while(firstRS.next() || secondRS.next())
{
// get the row of my first table
String firstRSRow = firstRS.getString("col1");
String secondRSRow = seconRS.getString("col1")
// compare row from first table to the row in second table
if(firstRSRow.startsWith(secondRSRow))
{
// do some processing here....
} // end if
} // end while
public static boolean resultset(String SQL1, String SQL2){
public boolean status=false;
ResultSet ViewResultset = st.executeQuery(SQL1);
ResultSet QueryResultset = st.executeQuery(SQL2);
while (QueryResultset.next() | ViewResultset.next())
if (ViewResultset.getRow() == 0 | QueryResultset.getRow() == 0) {
break;
} else {
for (int i = 1; i < ViewResultset.getMetaData().getColumnCount() + 1; i++) {
System.out.println("OOO"+ QueryResultset.getMetaData().getColumnCount());
String columnName = ViewResultset.getMetaData().getColumnName(i);
System.out.println("ColumnName :" + columnName);
for (int j = 1; j < QueryResultset.getMetaData().getColumnCount() + 1; j++)
if (ViewResultset.getMetaData().getColumnName(i).equals(QueryResultset.getMetaData().getColumnName(j))&& !(QueryResultset.getString(columnName) == null || ViewResultset.getString(columnName) == null))
if (ViewResultset.getString(columnName).toUpperCase().contains(QueryResultset.getString(columnName).toUpperCase())) {
System.out.println(" Actual "+ ViewResultset.getMetaData().getColumnName(i) + " Expected: "+ ViewResultset.getString(columnName));
status=true;
}
else {System.out.println(" Actual "+ ViewResultset.getMetaData().getColumnName(i) + " Expected: "+ ViewResultset.getString(columnName));
}
}
}
return status;
}
}
ResultSet is a subclass of Object ultimately and we can compare two or more objects directly. A little specific to your question:
ResultSet rs=st.executeQuery(sql1);
ResultSet rs1=st.executeQuery(sql2);
where sql1 and sql2 are 2 Statements
while(rs.next() && rs1.next()){
int a=rs.next();
int b=rs1.next();
if(a==b){
System.out.println("Compairing two ResultSets");
}
}