I have my below method which accepts two parameters-
userId and attributes Map
attributes Map will have column names and column values-
Let's take an example if I have 5 columns in the above map, then there will be 5 keys and 5 values as well.
so then my SQL will look like this-
String sql = "INSERT INTO PROFILE(userId, colA, colB, colC, colD, colE) VALUES ( ?, ?, ?, ?, ?, ?) ";
In the same way, my query statement will look like-
BoundStatement query = prBatchInsert.bind(userId, colAValue, colBValue, colCValue, colDValue, colEValue);
But in some case it might be possible that the attributes map will have 20 columns. So basis on that, I need to make sql and query statement.
Below is the code which almost assume that table will have four columns only which is not right.
public void upsertAttributes(final String userId, final Map<String, String> attributes) {
try {
String[] keys = (String[])attributes.keySet().toArray();
String sql = "INSERT INTO PROFILE(userId, "+keys[0]+", "+keys[1]+", "+keys[2]+", "+keys[3]+") VALUES ( ?, ?, ?, ?, ?) ";
BoundStatement query = prBatchInsert.bind(userId, attributes.get(keys[0]), attributes.get(keys[1]), attributes.get(keys[2]), attributes.get(keys[3]));
} catch (Exception e) {
LOG.error(e);
}
}
How can i write the above method more generic corresponding to the attributes Map?
You should use Spring jdbctemplate, it has loads of apis for this. Check out this link : http://static.springsource.org/spring/docs/2.0.8/api/org/springframework/jdbc/core/JdbcTemplate.html
EDIT:
Check this link : http://www.mkyong.com/spring/spring-named-parameters-examples-in-simplejdbctemplate/
It exactly does what you want.
EDIT: If you dont want to use Spring then in order to achieve this you need to generate complete sql string dynamically. Binding will not be used.
See this code[I have not run this code, but this is the basic idea to achieve this]:
public void upsertAttributes(final String userId, final Map<String, String> attributes) {
try {
String[] keys = (String[])attributes.keySet().toArray();
String sql = "INSERT INTO PROFILE(userId, "+keys[0]+", "+keys[1]+", "+keys[2]+", "+keys[3]+") VALUES ( ?, ?, ?, ?, ?) ";
StringBuffer keysStmt = new StringBuffer("INSERT INTO PROFILE("+userId);
StringBuffer valuesStmt = new StringBuffer(" VALUES (" );
Iterator itr = attributes.keySet().iterator();
while(itr.hasNext()){
String key = itr.next();
keysStmt.append(","+key);
valuesStmt.append(attributes.get(key)+",");
}
//remove last comma
valuesStmt = new StringBuffer(valuesStmt.toString().substring(0,valuesStmt.length-1));
sql = keysStmt.append(")")+valuesStmt.append(")");
} catch (Exception e) {
LOG.error(e);
}
}
Related
Is there any possibility to specify the sequence generator as a value for a column in SimpleJdbcInsert, JdbcTemplate, NamedParameterJdbcTemplate or any other class for batch execution?
e.g., I want to achieve the SQL to be generated by any of the above classes as:
INSERT INTO SOME_TABLE_NAME (ID, COLUMN_A,...) VALUES (SOME_SEQUENCE.NEXTVAL, 'value for column A', ...);
A sample code snippet is like:
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
List<SomeTableEntity> listOfEntities; // received from method parameter
SimpleJdbcInsert sql = new SimpleJdbcInsert(dataSource).withTableName("SOME_TABLE_NAME");
SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(listOfEntities.toArray());
sql.executeBatch(batch);
I tried to trick the SimpleJdbcInsert as:
SqlParameterSource id = new MapSqlParameterSource("ID", "SOME_SEQUENCE.nextval"));
SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(listOfEntities.toArray());
List<SqlParameterSource> params = new ArrayList<>(batch.length + 1);
params.add(id);
for (SqlParameterSource param : batch)
{
params.add(param);
}
sql.executeBatch(params.toArray(new SqlParameterSource[] {}));
But not to a surprise, that didn't worked since the ID column is of type numeric and it tried to fill the value as "SOME_SEQUENCE.nextval" instead of evaluating the result of SOME_SEQUENCE.nextval.
PS: There are too many columns in the table and due to that I do not want to use a prepared statement solution
String sql = "INSERT INTO USER
(USER_PK, ACCOUNTNUMBER, FIRSTNAME, LASTNAME, EMAIL )
VALUES
(user.nextval, ?, ?, ?, ?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, accountNumber);
ps.setString(2, firstName);
ps.setString(3, lastName);
ps.setString(4, email);
I created a small program where the user must input data into text fields and select options from combo boxes. I created a database using XAMPP and created the corresponding tables for the program through the web browser.
The database is called activitydb and the table that's responsible for storing the data from the program is called userdata.
The first column in the table is called UserID and is an int that auto increments every time a new entry is added.
The rest are all varchars with varying maximum lengths.
This is currently the source code of the program:
Connection con = null;
Statement st = null;
try {
// activitydb = database name
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/activitydb?zeroDateTimeBehavior=convertToNull", "root", "");
st = con.createStatement();
// userdata = table name
String sqlconn = "INSERT INTO userdata (UserID, LastName, FirstName, MiddleName, Email, Sex, HomeAddress, City, CPUBrand, ComputerType, HardwareSpecs, GPUBrand, GPUType, GPUVRAM)";
PreparedStatement prdStmt = con.prepareStatement(sqlconn);
// Input of variable data into corresponding database table
// First table will be declared as null as it is an Integer designed with an Auto Increment
prdStmt.setString(1, null);
prdStmt.setString(2, jTextLastName.getText());
prdStmt.setString(3, jTextFirstName.getText());
prdStmt.setString(4, jTextMiddleName.getText());
prdStmt.setString(5, jTextEmail.getText());
prdStmt.setString(6, jComboBoxSex.getSelectedItem().toString());
prdStmt.setString(7, jTextHomeAddress.getText());
prdStmt.setString(8, jTextCity.getText());
prdStmt.setString(9, jComboBoxCPUBrand.getSelectedItem().toString());
prdStmt.setString(10, jComboBoxComputerType.getSelectedItem().toString());
prdStmt.setString(11, jComboBoxHardwareSpecs.getSelectedItem().toString());
prdStmt.setString(12, jComboBoxGPUBrand.getSelectedItem().toString());
prdStmt.setString(13, jComboBoxGPUType.getSelectedItem().toString());
prdStmt.setString(14, jComboBoxGPUVRAM.getSelectedItem().toString());
// Do this if something goes wrong
} catch (SQLException err) {
// Print error message to console for diagnosis
System.out.println(err.getMessage());
}
For the combo boxes, I've used getSelectedItem().toString() to have the data found inside stored as a String.
Clicking the button will make the program do nothing but print this in the console:
Parameter index out of range (1 > number of parameters, which is 0).
You are missing the place holders ? to put the values, your query should be :
String sqlconn = "INSERT INTO userdata (UserID, LastName, FirstName, MiddleName," +
" Email, Sex, HomeAddress, City, CPUBrand, ComputerType, HardwareSpecs," +
" GPUBrand, GPUType, GPUVRAM) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
I am inserting into database as below.
private static final String INSERT_SQL = "INSERT INTO
ARTICLE_TAG_RELATION(ARTICLE_ID, TAG_ID) VALUES (?, ?)";
private final JdbcTemplate template;
//method 1
void addTags(String articleId, List<String> tags) {
// TODO Auto-generated method stub
for(String tag:tags){
template.update(INSERT_SQL, ps-> {
ps.setString(1, articleId);
ps.setString(2, tag);
});
}
}
//method 2
void addTags(String articleId, List<String> tags) {
template.update(
INSERT_SQL,
(/*PreparedStatement*/ ps) -> {
for(String tag:tags){
ps.setString(1, articleId);
ps.setString(2, tag);
}
});
}
which method is correct 1 or 2. Or are both the same. Also i dont understand the second method well.
The first method. BTW, this has nothing to do with lambdas. The second approach is going to keep on overwriting previous statement values and only the last tag is going to be inserted. Here is the same version of code without lambda:
getJdbcTemplate().update(INSERT_SQL, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, articleId);
ps.setString(2, tag);
}
});
Apparently loop inside setValues does not make sense.
In method 1 you generate many sql insert queries but you can insert multiple rows in one query.
Query syntax:
INSERT INTO
ARTICLE_TAG_RELATION(ARTICLE_ID, TAG_ID) VALUES (?, ?), (?, ?), (?, ?), (?, ?), ...
You need ArrayList list that holds String array of articleId and tag. In java 7 it would be like:
int position=1;
for (int i=0;i<list.size();i++) {
String[] articleTag = list.get(i);
preparedStatement.setString(position++,articleTag[0]);
preparedStatement.setString(position,articleTag[1]);
}
I have problems with inserting Integer array into Postgresql Table, how do I go about this?
String sql = "INSERT INTO draw_result (id, ball_numbers, balls_with_mega_ball, draw_dates, mega_plier) VALUES(?, ?, ?, ?, ?)";
Object[] params = {randomNumbers, ballNumbers, ballNumbersMegaBall, drawDates, megaPlier};
jdbcTemplate.update(sql, params);
Where ballNumbers and ballNumbersMegaBall are ArrayList. Filled with 2 digit numbers.
Here is the PostgreSQL table:
CREATE TABLE public.draw_result
(
id bigint NOT NULL,
draw_dates date,
ball_numbers bigint[],
balls_with_mega_ball bigint[],
mega_plier bigint,
CONSTRAINT draw_result_pkey PRIMARY KEY (id)
)
And here is the Error from Springboot :
There was an unexpected error (type=Internal Server Error, status=500).
PreparedStatementCallback; bad SQL grammar [INSERT INTO draw_result (id, >ball_numbers, balls_with_mega_ball, draw_dates, mega_plier) VALUES(?, ?, ?, ?, >?)]; nested exception is org.postgresql.util.PSQLException: Can't infer the SQL >type to use for an instance of java.util.ArrayList. Use setObject() with an >explicit Types value to specify the type to use.
Recently I had a similar problem. My solution:
public void setDrawResult(BigInteger id, List<BigInteger> ballNumbers, List<BigInteger> ballsWithMegaBall, Date drawDates,BigInteger megaPlier){
String sql = "INSERT INTO draw_result (id, ball_numbers, balls_with_mega_ball, draw_dates, mega_plier) VALUES(?, ?, ?, ?, ?)";
jdbcTemplate.update(sql
, id
, createSqlArray(ballNumbers)
, createSqlArray(ballsWithMegaBall)
, drawDates
, megaPlier
);
}
private java.sql.Array createSqlArray(List<BigInteger> list){
java.sql.Array intArray = null;
try {
intArray = jdbcTemplate.getDataSource().getConnection().createArrayOf("bigint", list.toArray());
} catch (SQLException ignore) {
}
return intArray;
}
I have this code that adds Product to Products table in the database but it is adding more than one row.
Here's the code:
public int addProduct(Products product, String supplierName) {
//find a product
String checkAllProducts = "SELECT * FROM products WHERE product_name = ?";
//Insert product and supplier id where supplier exist in suppliers table sql statement
String insertSql = "INSERT INTO products (product_name, product_type, supplier_id, number_of_stocks, price_per_unit, packaging_type) SELECT ?,?,suppliers.supplier_id,?,?,? FROM suppliers WHERE suppliers.supplier_name = ?";
//Get connection
Connection conn = DbUtil.getConnection();
//Resultset for checking existing products
ResultSet resultSet = null;
int inserted = 0;
try {
//Prepare check all products statement
allProducts = conn.prepareStatement(checkAllProducts);
allProducts.setString(1, product.getProductName());
resultSet = allProducts.executeQuery();
//If doesn't exist in products table
if (!resultSet.next()) {
//Prepare insert statement
addProduct = conn.prepareStatement(insertSql);
//Get product parameter's data
addProduct.setString(1, product.getProductName());
addProduct.setString(2, product.getProductType());
addProduct.setInt(3, product.getNumberOfStocks());
addProduct.setBigDecimal(4, product.getPricePerUnit());
addProduct.setString(5, product.getPackagingType());
addProduct.setString(6, supplierName);
//Confirm insert
int confirmation = JOptionPane.showConfirmDialog(null, "Are you sure you want to insert this product?", "Insert Confirm", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
if (confirmation == JOptionPane.YES_OPTION) {
//execute insert
inserted = addProduct.executeUpdate();
}
}//Else don't insert and show error messages.
else {
JOptionPane.showMessageDialog(null, "Product already exists.", "Invalid insert.", JOptionPane.ERROR_MESSAGE);
}
} catch (SQLException ex) {
Logger.getLogger(ProductDAO.class.getName()).log(Level.SEVERE, null, ex);
} finally {
DbUtil.close(conn, allProducts, resultSet);
DbUtil.close(conn, addProduct, resultSet);
}
return inserted;
}
As you can see in the code above, I'm checking if a product doesn't exist in the table then insert with confirmation. It's successfully adding but it's adding more than one row. For example, assume that I have initialised the productDao object and I test it out in this way: productDAO.addProduct(new Products("Hotdogs", "Full", 55, new BigDecimal(0.30), "Box"), "Wing Yip");. After doing that, it inserted 14 of these as you can see in the image's link below;
14 Duplicate Rows
Does anyone know why this is happening? Please let me know thanks.
The second part of your insert statement is a select statement:
SELECT
?, ?,
suppliers.supplier_id,
?, ?, ?
FROM products, suppliers
WHERE
products.supplier_id = suppliers.supplier_id
AND suppliers.supplier_name = ?
This has the ability to select all records that have that supplier name and which appear in both tables. Evidently that supplier has 14 products, so that is how many new records are inserted.
To fix this remove the implicit JOIN. It isn't needed for the supplier_id lookup.
INSERT INTO products (
product_name,
product_type,
supplier_id,
number_of_stocks,
price_per_unit,
packaging_type
) SELECT
?, ?,
suppliers.supplier_id,
?, ?, ?
FROM suppliers
WHERE suppliers.supplier_name = ?
Thanks to #Scary Wombat and #4castle for the help. What I did now is to create a separate statement and added more code as you can see below.
//find supplier
String checkSupplierQuery = "SELECT products.supplier_id FROM products, suppliers WHERE products.supplier_id = suppliers.supplier_id AND suppliers.supplier_name = ? ";
//Prepare check supplier statement
PreparedStatement checkSupplier = conn.prepareStatement(checkSupplierQuery);
checkSupplier.setString(1, supplierName);
resultSet = checkSupplier.executeQuery();
String supplier = "";
while (resultSet.next()) {
supplier = resultSet.getString("products.supplier_id");
}
This solved my problem but I'm trying with INSERT INTO SELECT statement which it didn't work as I expected. Thanks again for those who helped me. :-)