Java / MySQL and JSON_SET array not working - java

I am using MySQL / JSON to store userdata in the database. I have one userid column and a column named data. The data column is a json type.
The default value of this json column is "{}";
When I update the user in the application and save it to the database using JSON_SET('column', 'name', 'value'). Everything works fine. Examples:
UPDATE `users` SET `data` = JSON_SET(`data`, '$.lastname', 'lastname') WHERE `userid` = 1;
UPDATE `users` SET `data` = JSON_SET(`data`, '$.money', 120) WHERE `userid` = 1;
But when I try to set for example a list or hashmap, things starting to get tricky. I use json-simple and gson to parse all objects to json before saving them to the database. But when I do this with a list or hashmap I cant set it in the database (unless I set parse the json to a string with '' around it)
But when I do add the '', json deserialize doesn't see it as a list.
Hope you guys can help me out. thanks in advance.
Here is some code:
String query = "UPDATE `users` SET `data` = JSON_SET(`data`, '$." + fieldName + "', " + this.gson.toJson(value) + ") WHERE `userid` = '" + userId + "';";

Related

saveOrUpdate doesn't work in Spring Data with H2 database

I have for loop in my program where I save new objects to database. It looks like
for (String value: readvalue.readValue()) {
Value value= getValueForSomething(something);
System.out.println(value);
valueRepository.save(value);
}
And this fragment of code is executed every 30s and saving to database all values. Some values in database have two same fields and one other. How can I update values in h2 database instead of insert new?
I would suggest that within your for loop, create a method that checks to see if the object already exists in your H2 DB by querying for it using an unique identifier like an id. Use the following example RDB query as a reference:
private static final String PRODUCT_ALREADY_EXISTS_QUERY = "SELECT EXISTS(SELECT 1"
+ " FROM inventory.products "
+ " WHERE 1 = 1"
+ " AND id = :id)";
Then, if the record does exists, then call an update method that utilizes a query to UPDATE using the unique identifier. An RDB example query would be:
private static final String UPDATE_QUERY = "UPDATE inventory.products"
+ " SET (company_id, name, price, type, quantity, created_date, last_modified_date) = "
+ " (:companyId, :productName, :price, :productType, :quantity, :createdDate, :lastModifiedDateTime)"
+ " WHERE id = :id ";
If the record doesn't exist, then just create the record like you are.

Liquibase loading data from csv

I want to load an entire column of my PostgreSql table with data from csv file but when I do that I get an exception saying that the primary key of my table should not be null. It looks like Liquibase is creating new rows to insert the data. Is there a way to load the data in existing rows ?
DatabaseChangeLog dbChangeLog = new DatabaseChangeLog();
Liquibase liquibase = new Liquibase(dbChangeLog, new FileSystemResourceAccessor(), database);
ChangeSet loadChangeSet = new ChangeSet(id + "", "nasri", false, false, "", "", "", liquibase.getDatabaseChangeLog());
LoadDataChange loadDataChange = new LoadDataChange();
loadDataChange.setTableName(key);
loadDataChange.setChangeSet(loadChangeSet);
loadDataChange.setResourceAccessor(new FileSystemResourceAccessor());
String path = context.getBundle().getVersion() + "." + key + "." + columnKey + "." + targetFieldKey + ".csv";
loadDataChange.setFile(path);
loadDataChange.setSchemaName("public");
LoadDataColumnConfig columnConfig = new LoadDataColumnConfig();
columnConfig.setName(targetFieldKey);
columnConfig.setType("String");
loadDataChange.addColumn(columnConfig);
loadChangeSet.addChange(loadDataChange);
liquibase.getDatabaseChangeLog().addChangeSet(loadChangeSet);
liquibase.update("");
There is a class thats called: LoadUpdateDataChange
The description says:
Loads or updates data from a CSV file into an existing table. Differs from loadData by issuing a SQL batch that checks for the existence of a record. If found, the record is UPDATEd, else the record is INSERTed. Also, generates DELETE statements for a rollback.
Looks like it should do what you are looking for (I have not used this myself though).
To update records, LoadUpdateDataChange is not working for me. Its always trying to insert new records.It won't update existing records.
If want to update existing records, you should use like below.
<update tableName="someTable">
<column name="update_column_name" value="updated value" />
<where> primaryKey = condition</where>
</update>

How to use generated id in SQL statement?

I am trying to add a new record to a table from Derby database. I need to use the record number in one of its fields. E.g. I need to save a path to the image in photo field. And the name of the image must corresponds its id. For example 1.jpg. I was trying this statement (file extention is not used in this example):
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO Users (name, lastname, email, address, password, photo, lastvisit, status) VALUES ('" + name + "','" + lastname + "','" + email + "','" + address + "','" + DigestUtils.toMd5(password) + "', id, '" + now + "','user')");
But I get an error:java.sql.SQLSyntaxErrorException: Column 'ID' is either not in any table in the FROM list...
But this column definitely exists. What is the reason of the error?
Well the short answer imho is that you cannot do that in SQL.
In your table Users, I guess that your id column is an auto incremented primary key. What you can do is insert your record and then get the last id generated for this insert statement. Then you have to perform an update query to set the value of your photo column.
By the way, definitely learn to use placeholders in prepared statements
I'm not a specialist of Derby but browsing the doc I found how you can get the generated columns.
pstmt.execute(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet keys = pstmt.getGeneratedKeys();
keys.next();
int id = keys.getInt();

JDBC Query Clarification

I have an insert query for Sybase Db which inserts one varchar value and 2 integer values into a table which is returning a error.
The table column datatypes are Varchar,Int,Int
The query is as shown below
sql_str = "INSERT INTO TempTable VALUES(" +
"'"+ 'Y' +"',"+ "DateDiff(ss,'12/31/1969 19:00:00','"+ secBegin +"')" +","+ "DateDiff(ss,'12/31/1969 19:00:00','"+ secEnd +"')" +")";
and its uses 2 String values i.e. secBegin and secEnd
String secBegin = "04/03/2012 10:08:24";
String secEnd = "04/03/2012 10:09:07";
And I'm getting the following error:
com.sybase.jdbc3.jdbc.SybSQLException: Insert error: column name or number of supplied values does not match table definition.
and its pointing to the insert query shown above as the erreneous line.
Is there any error in the jdbc query shown above.
Hi you can try this should it work :
sql_str = "INSERT INTO TempTable VALUES("Y",DateDiff(ss,'12/31/1969 19:00:00','"+ secBegin +"'),DateDiff(ss,'12/31/1969 19:00:00','"+ secEnd +"'))";

Hibernate and PostgreSQL: Can I configure GenerationType.IDENTITY, where each id column's default value comes from a shared sequence?

Ok I am not 100% sure if I really want this but currently it seems like a good idea.
I configure my entities like this:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id = 0;
If I let Hibernate generate the table, the result is:
id bigserial NOT NULL,
CONSTRAINT test_pkey PRIMARY KEY (id)
The bigserial in turn translates into a bigint with a default value of:
nextval('test_id_seq'::regclass)
Now that's ok but I am currently considering using a single sequence for all tables and would like the default value to be:
nextval('my_global_sequence'::regclass)
Maybe I could go and manually adjust the default value later, but do you have any ideas how I could tell hibernate to define the table like this automatically? Is there maybe a way to push my own create table sql strategy that would issue code like this for the id column (untested SQL syntax):
id bigint NOT NULL DEFAULT = nextval('my_global_sequence'::regclass)
Note that I would like to stick with the IDENTITY strategy, because I want to avoid any "hi/lo multiplying allocation size cache sequence whatever"-strategies. From time to time I need to insert some manual rows through pgAdmin without risking any id conflict with hibernate. All the other strategies seem like magic to me (= I don't understand them).
I didn't figure out how to do this with hibernate. My current solution is just a plain old JDBC/SQL logic that I execute on every startup. So far I am pretty happy about it.
Connection connection = DriverManager.getConnection(dbUrl, dbUser, dbPassword);
String catalog = connection.getCatalog();
String schema = "public";
DatabaseMetaData databaseMetaData = connection.getMetaData();
ResultSet tablesResultSet = databaseMetaData.getTables(catalog, schema, null, new String[] { "TABLE" });
while(tablesResultSet.next())
{
String tableName = tablesResultSet.getString("TABLE_NAME");
LOGGER.info("updating table: '" + tableName + "', setting global sequence");
// update primary keys to global sequence
{
// LOGGER.info("setting global sequence for '" + tableName + "'.id");
connection.createStatement().execute("ALTER TABLE \"" + tableName + "\" ALTER COLUMN id TYPE bigint;");
connection.createStatement().execute("ALTER TABLE \"" + tableName + "\" ALTER COLUMN id SET NOT NULL;");
connection.createStatement().execute("ALTER TABLE \"" + tableName + "\" ALTER COLUMN id SET DEFAULT nextval('my_global_sequence'::regclass)");
}
}
You can use an annotation on your id to determine the sequence generator you wish to use:
Using your code as an example:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#SequenceGenerator(name="my_global_sequence")
private long id = 0;
With this annotation it is simple to configure specific sequence generators for each entity or group of entities, or even use the same everywhere.
The caveat is that you have to define that annotation for every entity you have

Categories