liquibase + jdbctemplate update statement - autoincrement issue - postgresql

In liquibase changset I am inserting some record to database
<changeSet id="test" author="xyz">
<insert tableName="testtable">
<column name="id" value="1"/>
<column name="name" value="testdata"/>
</insert>
</changeSet>
Then using jdbcTemplate I am trying to insert new rows using instance update() method.
jdbcTemplate.update(
"INSERT INTO test.testtable(name) VALUES (?)",
new Object[] {
someObject.getName()
});
When running above method for the first time, I am getting error that record with this ID already exists in the table. However when I repeat operation , I succeed with incremented ID with value 2.
How to integrate liquibase and jdbcTemplate together to eliminate this problem? I would expect that jdbcTemplate will somehow recognize that this ID 1 is already occupied and insert data with incremented, non-conflicting, unique ID.
I am using postgres.
Is there any option to do it without removing liquibase entry with hardcoded ID value?

I would expect that jdbcTemplate will somehow recognize that this ID
1 is already occupied and insert data with incremented,
non-conflicting, unique ID.
This would not happen. Liquibase looks to see if the changeset has been ran in the environment (by checking to see if the changeset id is present in the databasechangelog table). If it is not present, it will run the changeset. Just that simple, Liquibase is not doing any thinking for you, you have to make sure that the insert will work in any reasonable condition.
I did like the suggestion in the comment above to not insert an id but instead use an auto-increment if you are wanting to always insert and have the id increment.

Related

Mybatis Generator Postgres return created id support

I'm using postgres for a project that I just started to work on, and I realized that Mybatis provide support to retrieve the autogenerated id keys for many databases but unfortunately postgres is not one of them, I modified a little bit the generated sql mappers and:
- changing select instead of insert in the generated xml
- add "RETURNING id" as last line of each sentence
so it gonna look like:
<select id="insert" keyColumn="id" keyProperty="id" parameterType="com.myproject.Order" ...
insert into ...
...
RETURNING id
</select>
with that change it works like a charm but the problem is that as soon as the generator is executed again the changes should be applied again manually, I was looking for a plugin to help me to automate the changes but I did not found any that seems to help, did you do any time before something similar? what would be the recommendation?
For the explanation, I created a table users in a schema mbtest.
create table mbtest.users (
id serial primary key,
name varchar(20)
);
The below is the <table /> element in the generator config.
(in the comment, I wrote sqlStatementType, but it should be sqlStatement)
<table tableName="users" domainObjectName="User"
schema="mbtest">
<generatedKey column="id" sqlStatement="JDBC" />
</table>
The generated 'UserMapper.xml' contains the following insert statement.
Note that useGeneratedKeys, keyColumn and keyProperty populated by the <generatedKey /> above.
<insert id="insert" keyColumn="id"
keyProperty="id" parameterType="test.model.User"
useGeneratedKeys="true">
<!--
WARNING - #mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Sun Dec 08 12:36:30 JST 2019.
-->
insert into mbtest.users (name)
values (#{name,jdbcType=VARCHAR})
</insert>
(As the WARNING says, you should not modify the generated statements)
The insert statement will set the generated key to the id property of User.
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setName("User1");
mapper.insert(user);
sqlSession.commit();
assertNotNull(user.getId());
}
Here is a demo project so that you can verify yourself.

How to set Ignore Duplicate Key in Postgresql while table creation itself

I am creating a table in Postgresql 9.5 where id is the primary key. While inserting rows in the table if anyone tries to insert duplicate id, i want it to get ignored instead of raising exception. Is there any way such that i can set this while table creation itself that duplicate entries get ignored.
There are many techniques to resolve duplicate insertion issue while writing insertion query i.e. using ON CONFLICT DO NOTHING, or using WHERE EXISTS clause etc. But i want to handle this at table creation end so that the person writing insertion query doesn't need to bother any.
Creating RULE is one of the possible solution. Are there other possible solutions? Maybe something like this:
`CREATE TABLE dbo.foo (bar int PRIMARY KEY WITH (FILLFACTOR=90, IGNORE_DUP_KEY = ON))`
Although exact this statement doesn't work on Postgresql 9.5 on my machine.
add a trigger before insert or rule on insert do instead - otherwise has to be handled by inserting query. both solutions will require more resources on each insert.
Alternative way to use function with arguments for insert, that will check for duplicates, so end users will use function instead of INSERT statement.
WHERE EXISTS sub-query is not atomic btw - so you can still have exception after check...
9.5 ON CONFLICT DO NOTHING is the best solution still

How to enforce column order with Liquibase?

Setup:
liquibase 3.3.5
PostgreSQL 9.3
Windows 7 Ultimate
Having set the Liquibase.properties file with
changeLogFile = C://temp/changeset.xml,
I created a diff file with Liquibase (3.3.5).
liquibase.bat diffChangeLog
Examination of the changeset.xml file shows
-<addColumn tableName="view_dx">
<column type="int8" name="counter" defaultValueNumeric="0" defaultValue="0"/>
</addColumn
Problem is when
liquibase.bat update
is run, the changed table is NOT in the same column order as the reference table. This causes issues with the stored procedures using SETOF to return table rows.
Without destroying the existing table on the target database, how can column order be enforced using Liquibase?
TIA
I don't think that you can, in general, get Liquibase to enforce a column ordering. You will probably need to change your stored procedures to use column names rather than relying on position, which is a good habit to get into anyway.
Have you tried using afterColumn attribute of addColumn tag ?

Liquibase/PostgreSQL: how to preserve table case correctly?

I'm using Liquibase 3.1.1 to create tables in PostgreSQL 9.1. For example:
<changeSet id="1" author="bob">
<createTable tableName="BATCHES">
<!-- .. -- >
</createTable>
</changeSet>
However, the table gets created with a lowercase name:
# select * from "BATCHES";
ERROR: relation "BATCHES" does not exist
Is there any way to have Liquibase generate DDL that preserves the case of the table (and column etc) names that I specify in the change log?
You can use the objectQuotingStrategy="QUOTE_ALL_OBJECTS" attribute on your changeSet attribute or on the databaseChangeLog root element to override the default logic of "only quote objects that have to be"

Insert after delete same transaction in Spring Data JPA

Using Spring Data JPA I have the next flow inside the same transaction (REQUIRES_NEW) :
Remove a set of user's predictions with this Spring Data JPA repository method.
#Query(value = "DELETE FROM TRespuestaUsuarioPrediccion resp WHERE resp.idEvento.id = :eventId AND resp.idUsuario.id = :userId")
#Modifying
void deleteUserPredictions(#Param("userId") int userId, #Param("eventId") int eventId);
Insert the new user's predictions and save the master object (event).
eventRepository.save(event);
When this service finishes, the commit is made by AOP but only works in first attemp...not in the next ones...
How can I manage this situation without iterating over event's predictions entries and updating each one inside?
UPDATE
I tried with that and it doesn't work (the adapter inserts the objects I remove before):
#Transactional(propagation=Propagation.REQUIRES_NEW, rollbackFor=PlayTheGuruException.class)
private void updateUserPredictions(final TUsuario user, final TEvento event, final SubmitParticipationRequestDTO eventParticipationRequestDTO)
{
eventRepository.deleteUserPredictions(user.getId(), event.getId());
EventAdapter.predictionParticipationDto2Model(user, event, eventParticipationRequestDTO);
eventRepository.save(event);
}
Hibernate changed order of the commands. It works in below order :
Execute all SQL and second-level cache updates, in a special order so that foreign-key constraints cannot be violated:
1. Inserts, in the order they were performed
2. Updates
3. Deletion of collection elements
4. Insertion of collection elements
5. Deletes, in the order they were performed
And that is exactly the case. When flushing, Hibernate executes all inserts before delete statements.
The possible option are :
1. To call entityManager.flush() explicitly just after the delete.
OR
2. Wherever possible update existing rows and from rest create ToBeDeleted List. This will ensure that existing records are updated with new values and completely new records are saved.
PostgreSQL (and maybe other databases as well) have the possibility to defer the constraint until the commit. Meaning that it accepts duplicates in the transaction, but enforces the unique constraint when committing.
ALTER TABLE <table name> ADD CONSTRAINT <constraint name> UNIQUE(<column1>, <column2>, ...) DEFERRABLE INITIALLY DEFERRED;