diffChangelog creates redundant changesets - spring

In my project we have a base entity, which other entities extend from
public abstract class BaseEntity {
#Column(updatable = false, nullable = false)
#GeneratedValue
#Id
UUID id;
#Version private long version;
#CreationTimestamp
#Column(nullable = false, updatable = false, columnDefinition = "TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime created;
#UpdateTimestamp
#Column(nullable = false, updatable = true, columnDefinition = "TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime updated;
}
every time diffChangelog is run it creates new changesets addDefaultValue even though these were already set at table creation
databaseChangeLog:
- changeSet:
id: 1660844241773-1
author: bob (generated)
changes:
- addDefaultValue:
columnDataType: timestamp WITH TIME ZONE
columnName: created
defaultValueComputed: CURRENT_TIMESTAMP
tableName: foo
- changeSet:
id: 1660844241773-2
author: bob (generated)
changes:
- addDefaultValue:
columnDataType: timestamp WITH TIME ZONE
columnName: created
defaultValueComputed: CURRENT_TIMESTAMP
tableName: baz
- changeSet:
id: 1660844241773-3
author: bob (generated)
changes:
- addDefaultValue:
columnDataType: timestamp WITH TIME ZONE
columnName: created
defaultValueComputed: CURRENT_TIMESTAMP
tableName: bar
liquibase.properties
liquibase.hub.mode=off
url=jdbc:postgresql://localhost:5432/foo
driver=org.postgresql.Driver
changelogFile=src/main/resources/db/db.changelog-master.yml
referenceDriver=liquibase.ext.hibernate.database.connection.HibernateDriver
referenceUrl=hibernate:spring:com.foo\
?dialect=org.hibernate.dialect.PostgreSQL10Dialect\
&hibernate.physical_naming_strategy=org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy\
&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
Using liquibase-gradle-plugin v. 2.1.1
Liquibase versions
implementation 'org.liquibase:liquibase-core:4.14.0'
implementation 'org.liquibase.ext:liquibase-hibernate5:4.14.0'
liquibaseRuntime 'org.liquibase:liquibase-core:4.14.0'
liquibaseRuntime 'org.postgresql:postgresql:42.3.6'
liquibaseRuntime 'info.picocli:picocli:4.6.3'
runtimeClasspath 'org.yaml:snakeyaml:1.30'
liquibaseRuntime 'org.liquibase.ext:liquibase-hibernate5:4.14.0'
liquibaseRuntime 'org.springframework.boot:spring-boot-starter-data-jpa:2.7.2'
Why are these changeset created, and is there any way to avoid them being generated?

It's likely a bug in the hibernate implementation. You can tell what liquibase is seeing as differences by running a diff operation. The diff command will tell you what is different, whereas diffChangeLog will take those differences and try to figure out how to resolve them.
It could be an issue with it incorrectly seeing differences, or it could be an issue with not correctly resolving the seen differences.
You'd be best off creating an issue at https://github.com/liquibase/liquibase-hibernate/issues including what diff is reporting as the differences for you.

Related

Hibernate detached entity passed to persist error in Spring Boot in Kotlin using OneToOne with MapsId

I have the following entities in a fairly simple and straightforward Spring Boot application in Kotlin:
#Entity
class Target(
#Id #GeneratedValue var id: Long? = null,
// ... other stuff
)
#Entity
class Ruleset(
#OneToOne(fetch = FetchType.LAZY) #MapsId
var target: Target,
#Id #GeneratedValue var id: Long? = null,
// ... other stuff
)
And I have the following code to create them upon startup of a #Component:
#PostConstruct
#Transactional
fun init() {
val target = Target()
targetRepository.save(target)
val rule = Ruleset(target)
rulesetRepository.save(rule)
}
And when this runs I get the "detached entity passed to persist: com.mystuff.Target" error. I've used this approach in the past (see here: https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/) without issue, although never in trying to create them at the same time in the same method. I've also tried using the entity passed back by the .save() call on the Target repository in the persist of the Ruleset object with no success.
I am able to fix this if I go back to the "normal" way of doing a OneToOne relationship:
#Entity
class Target(
#OneToOne(mappedBy = "target", cascade = [CascadeType.ALL],
fetch = FetchType.LAZY, optional = false)
var ruleset: Ruleset?
#Id #GeneratedValue var id: Long? = null
)
#Entity
class Ruleset(
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "target_id")
var target: Target,
#Id #GeneratedValue var id: Long? = null,
)
But this is annoying as it forces me to pass a null into the Target constructor and then update it immediately after creating the Ruleset. I can't figure out why the other, simpler approach doesn't work.

Race Condition in Postgres SQL using Spring data JpaRepository

I am facing a wierd issue in my implementation where I am persisitng data to a PostgresSQL DB using Spring data JpaRepository
In my Entity class I have the below columns:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private int id;
#Column(name = "field1", nullable = false, length = 16)
private String field1;
#Column(name = "field2", nullable = false, length = 16)
private String field2;
#Column(name = "field3", nullable = false, length = 16)
private String field3;
I initially avoided declaring the fields above as composite since there were many fields to be dealt with as composite keys. I thought the java code check would do the trick in all scenarios
So basically, I have to maintain the uniqueness of each row based on field1,field2 and field3. That was the basic requirement for which I had checks in my java code that if any entry exists in the DB for the combination of field1,field2 and field3 then I used to throw java exceptions
No two rows can have these values repeating. All was good until the application was tested under some errorneous business scenarios which would never happen in production but got run by mistake
Whats happening now is that if 2 requests are triggered at the exact same instance with the exact same 3 fields above (through a script) then they both enter into the Database since both get the entry check as false
Would declaring all of them as one composite key resolve the situation?
You should define the unique constraint in your database in addition of JPA constraint.
#Entity
#Table(uniqueConstraints={
#UniqueConstraint(columnNames = {"field1", "field2", "field3"})
})
public class MyEntity {
...
}

spring jpa auditing with localdate

I want to get a monthly list, weekly list according to created date but JPA doesn't support LocalDate.
My Code like this:
UserDao:
List<User> findByCreatedAtBetween(LocalDate start, LocalDate end);
UserEntity:
#Column(nullable = false, updatable = false)
#Temporal(TemporalType.TIMESTAMP)
#CreatedDate
private LocalDate createdAt;
#Column(nullable = false)
#Temporal(TemporalType.TIMESTAMP)
#LastModifiedDate
private LocalDate updatedAt;
But createdAt and updatedAt only supports java.util.Date. Why doesn't it support java.time.LocalDate?
As you found out LocalDate is currently not supported by Spring Data Envers, or actually in Spring Data Commons where the relevant code resides.
The reason for this is that these timestamp fields are intended to, well, be time stamps, i.e. specify points in time. LocalDate doesn't do that. The problem is not so much the lack of accuracy, but that LocalDate and its sibling LocalDateTime do not define an ordering. It is perfectly possible that something that happened on the Jan 1st, 1970 happened after something else that happened on Jan 2nd, 1970. For more detail refer to the JavaDoc or to http://blog.schauderhaft.de/2018/03/14/dont-use-localdatetime/
Note that LocalDateTime is supported, but mostly because that was implemented before we did realize that it is the wrong type to use for this purpose.
I highly recommend using Instant as the type for this kind of fields.

oracle with spring boot not fetching by primary key

I have written a spring boot app with oracle db.
Below is my entiry class.
#Entity
public class SystemTypeLookup{
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
#Type(type = "uuid-char")
#Column(name = "ID", updatable = false, nullable = false)
protected UUID id;
#Column(name = "CODE")
private String code;
}
And in passing my own UUID as primary key value.
In oracle db ID is considered as RAW and the UUID stored in oracle is differently.
There is no - separation in oracle and all the UUID chars are in upper case.
When i try to find the entity using primary key it is not fetching the row with id. I'm always getting null.
#Resource(name = "coreRepository")
private ErpEntityRepository coreRepositoryBase;
SystemTypeLookup systemTypeLookup = coreRepositoryBase.findOne("WHERE o.id='"+id+"'", SystemTypeLookup.class);
when is pass 76c03cd9-3d96-40c5-8df9-aad8f2369453 as id value then the oracle will insert the id without '-' and all chars will be in upper case.
So how to solve this issue?
First of all you should use parameters in your query and second make sure that the id you are passing is of type UUID and not String.

data version dosen't increase when we delete or add an child entity in spring data?

I'm using #version annotation in spring data so I have a parent entity, and it has list of child entity. when I delete an element from child list the parent version doesn't increase. can anyone clarify for me this #version alternative,
why the versing in this case doesn't increase, is it a good way to manage versioning or should I use trasaction "lock".
in the documentation i read that the version update only on updating a row
in the databse but in my case i put version on parent entity and i want
note: i searched a lot in the internet but i didnt find a clear solution, can any one help me.
I assume you are using Hibernate. Lets say that the "UnderlyingPerTradingAccount" table has a column called "trading_account_id", which is a foreign key to the TradingAccount table. In order to achieve the behavior you described, you need to change the mapping. Can you try this:
public class TradingAccount {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="trading_account_id", referencedColumnName = "trading_account_id", insertable = false, updatable = false)
private List<UnderlyingPerTradingAccount> underlyingPerTradingAccounts;
#Version
private Long version;
}
and
public class UnderlyingPerTradingAccount {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name="trading_account_id", nullable = false)
private TradingAccount tradingAccount;
private Boolean enableBuy;
private Boolean enableSell;
}
This should mark the parent entity as "dirty" when the child entity is updated and trigger the version increment.
However, I would think of some other method to track "version" changes of the parent entity as this would just cause an additional overhead and update statements to the parent.

Resources