Spring JPA using database timestamp for inserts - spring

I am using Spring JPA
#Column(name = "rec_cren_tm", insertable = false, updatable = false)
private Timestamp recCrenTm;
which is using database timestamp and inserting properly.
But while fetching I am getting this is property as null. Below is what I am using on the method
#Transactional
public interface IClaimRepository extends JpaRepository<Claim, Long> {
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
List<Claim> findByLnId(String LnId);
How do I get this?

This is the same answer i am looking for How to map timestamp column to JPA type?
But this thread doesnt have solution i am looking for

Related

Hibernate 6 #Type annotation

I upgraded to Springboot 3.0 and in my hibernate entity class has something like:
#Column(columnDefinition = "jsonb", nullable = false, updatable = true, name = "accounts")
#Type(type = "jsonb")
private ArrayList<Account> accounts;
But I'm getting the exception 'Cannot resolve method 'type' since upgrading to Springboot 3.0 and moving to Jakarta persistence.
I need a replacement for com.vladmihalcea.hibernate.type.json.JsonBinaryType;
I found the answer:
I had to use the lib:
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-60</artifactId>
and in the entity:
#Column(columnDefinition = "jsonb", nullable = false, updatable = true, name = "accounts")
#Type(JsonBinaryType.class)
private ArrayList<Account> accounts;
In Hibernate 6, the mapping annotations are much more typesafe. You're usually required to specify Class references instead of stringly-typed names.
If you want to model JSON, I would suggest you to use #JdbcTypeCode(SqlTypes.JSON) which is how you model JSON with plain Hibernate ORM Core. Also see https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#basic-mapping-json

Hibernate search does not remove old value from lucene index when the object is deleted via an #NoRepositoryBean Jpa method

I have a NoRepositoryBean Jpa interface that has one custom jpa method called deleteAllByIdIn(...) which is inherited by some concrete JpaRepositories. For some reason this custom delete method is ignored by Hibernate Search. Whenever an entity is deleted through this custom method its value is not removed from the lucene index after the delete is done. I will explain the problem some more further down this post; but first here's the code
#NoRepositoryBean
public interface NameTranslationDao<T extends NameTranslation> extends JpaRepository<T, Long> {
#Modifying
#Transactional
#Query(value = "DELETE FROM #{#entityName} c WHERE c.id IN :translationsToDelete")
public void deleteAllByIdIn(#Param("translationsToDelete") Set<Long> translationsToDelete);
}
Heres a JpaRepository subclass that extends this interface:
#Repository
#Transactional(readOnly = true)
public interface LifeStageCommonNameTranslationDao extends CommonNameTranslationDao<LifeStageCommonNameTranslation> {
}
Theres another #NoRepositoryBean interface in-between the concrete JpaRepository and the NameTranslationDao NoRepositoryBean. That one is called CommonNameTranslationDao but it doesn't override the custom method in any way, so it is unlikely the cause of the problem, nevertheless heres the code of that repository:
#NoRepositoryBean
public interface CommonNameTranslationDao<T extends NameTranslation> extends NameTranslationDao<T> {
#Deprecated
#Transactional(readOnly = true)
#Query("SELECT new DTOs.AutoCompleteSuggestion(u.parent.id, u.autoCompleteSuggestion) FROM #{#entityName} u WHERE u.autoCompleteSuggestion LIKE :searchString% AND deleted = false AND (u.language.id = :preferredLanguage OR u.language.id = :defaultLanguage)")
List<AutoCompleteSuggestion> findAllBySearchStringAndDeletedIsFalse(#Param("searchString") String searchString, #Param("preferredLanguage") Long preferredLanguage, #Param("defaultLanguage") Long defaultLanguage);
#Transactional(readOnly = true)
#Query(nativeQuery = true, value = "SELECT s.translatedName FROM #{#entityName} s WHERE s.language_id = :preferredLanguage AND s.parent_id = :parentId LIMIT 1")
public String findTranslatedNameByParentAndLanguage(#Param("preferredLanguage") Long languageId, #Param("parentId") Long parentId);
#Modifying
#Transactional
#Query(nativeQuery = true, value = "DELETE FROM #{#entityName} WHERE id = :id")
void hardDeleteById(#Param("id") Long id);
#Modifying
#Transactional
#Query(nativeQuery = true, value = "UPDATE #{#entityName} c SET c.deleted = TRUE WHERE c.id = :id")
void softDeleteById(#Param("id") Long id);
}
Also, heres the code of the LifeStageCommonNameTranslation entity class:
#Entity
#Indexed
#Table(
uniqueConstraints = {
#UniqueConstraint(name = "UC_life_cycle_type_language_id_translatedName", columnNames = {"translatedName", "parent_id", "language_id"})
},
indexes = {
#Index(name = "IDX_lifestage", columnList = "parent_id"),
#Index(name = "IDX_translator", columnList = "user_id"),
#Index(name = "IDX_species_language", columnList = "language_id, parent_id, deleted"),
#Index(name = "IDX_autoCompleteSuggestion_language", columnList = "autoCompleteSuggestion, language_id, deleted")})
public class LifeStageCommonNameTranslation extends NameTranslation<LifeStage> implements AuthorizationSubject {
#Id #DocumentId
#GenericGenerator(
name = "sequenceGeneratorLifeStageCommonNameTranslation",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
#org.hibernate.annotations.Parameter(name = "sequence_name", value = "_lifestagecommonnametranslation_hibernate_sequence"),
#org.hibernate.annotations.Parameter(name = "optimizer", value = "pooled"),
#org.hibernate.annotations.Parameter(name = "initial_value", value = "1"),
#org.hibernate.annotations.Parameter(name = "increment_size", value = "25"),
#org.hibernate.annotations.Parameter(name = "prefer_sequence_per_entity", value = "true")
}
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "sequenceGeneratorLifeStageCommonNameTranslation"
)
#Field(analyze = Analyze.NO, store = Store.YES, name = "parentId")
private Long id;
#IndexedEmbedded(includeEmbeddedObjectId = true)
#ManyToOne(fetch = FetchType.LAZY)
private LifeStage parent;
#Field(index = NO, store = Store.YES)
private String autoCompleteSuggestion;
//Getters and setters ommitted
The problem is the following: Whenever i use the inherited deleteAllByIdIn() method on LifeStageCommonNameTranslationDao then Hibernate Search will not remove the autoCompleteSuggestion field value from the lucene index after the entity has been deleted. If however i use the standard deleteById() JpaRepository method to delete the entity then the field value is removed from the lucene index.
Both the custom and the standard delete method were called within a #Transactional annotated method and i also called the flush() jpaRepository method right afterwards. I did this because I've read that this can sometimes help to update the lucene index. But in the case of deleteAllByIdIn() calling flush() afterwards did not help at all.
I already ruled out the possiblity that the problem was caused by the spEL expression in the SQL query. I tested this by replacing #{#entityName} with a concrete entity name like LifeStageCommonTranslation and then calling the deleteAllByIdIn() delete method. But the problem still persisted. The lucene index still did not remove the autoSuggestionText field value after the delete.
I can easily solve this problem by simply using the standard jpa method deleteById() but i want to know why the custom made jpa method deleteAllByIdIn() does not cause Hibernate search to update the lucene index.
Hibernate Search detects entity change events happening in your Hibernate ORM Session/EntityManager. This excludes insert/update/delete statements that you wrote yourself in JPQL or native SQL queries.
The limitation is documented here: https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#limitations-changes-in-session
The workaround is documented there too:
One workaround is to reindex explicitly after you run JPQL/SQL queries, either using the MassIndexer or manually.
EDIT: And of course your workaround might be valid as well, if deleteById loads the entity in the session before deleting it (I'm not that familiar with the internals of Spring Data JPA):
I can easily solve this problem by simply using the standard jpa method deleteById() but i want to know why the custom made jpa method deleteAllByIdIn() does not cause Hibernate search to update the lucene index.

Postgresql Spring Data #GeneratedValue without #Id return null value

im working in spring boot project i want to map a property with serial (using my sequence) column in my table and this column is not the ID.
i found this solution :
#Column(name = "DEMANDE_NUMBER", insertable = false, updatable = false, columnDefinition = "serial")
private Integer demandeNumber;
(because #GeneratedValue persist null and doesn't use the sequence)
this solution works fine and the field is persisted in my DB and the value uses the sequence but when i get my object after saving using my repository the demandeNumber is null
Demande savedDemande= demandeRepository.save(demandeToSave);
//demandeObj .getDemandeNumber() return null
any suggestion to resolve this issue please ?
Thanks.
according to this answer How to use a sequence generator for a non ID field?
i added the following annotation on my property
#Generated(GenerationTime.INSERT)
#Column(name = "column_name", columnDefinition = "serial", updatable = false)
you should import the package from hibernate and not javax.persistence.
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime;
i hope this can help other people in the future. note this solution is for spring data with postgresql.

Spring data #ReadOnlyProperty causing unexpected behavior

I have a Model attribute that needs to set #ReadOnlyProperty so that it won't persist after first inserting the line.
Assume my model like below
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false)
#JsonIgnore
private Long id;
#Column(unique = true, nullable = false)
#ReadOnlyProperty
private String openId;
}
then I have a UserRepository:
public interface UserRepository extends JpaRepository<User, Long> {
}
then I provide 2 Restful API for POST and PUT.
The create user operation code is as simple as below:
user.setOpenId(1);
userRepository.save(user)
The update user operation is almost the same:
user.setOpenId(2);
user = userRepository.save(user);
I'm surprised that the user's openId attribute will be changed, after POST and then PUT, the returned user object will have the changed value.(user.getOpenId() == 2)
It looks like #ReadOnlyProperty not working, I'm using the RELEASE version of spring-boot-starter-data-jpa. Can someone help explain?
It seems that #ReadOnlyProperty doesn't work. The following bug report is open for years:
Properties with #ReadOnlyProperty annotation are being nullified in PATCH requests
If you want to deny modifying the property via Spring Data Rest endpoints, use the #JsonProperty(access = Access.READ_ONLY) annotation. It affects the JSON deserialization, so the annotated property never reaches Spring Data Rest.
If you also need to deny the writing of the property via Spring Data JPA, you can use the following JPA annotation: #Column(updatable=false) It denies the override on the underlaying JPA level, instead of Spring Data JPA level.

How to Seed Data to H2 DB in Spring Boot App with JPA

A little bit of background:
I work on a Spring boot app in a large corporation and we have no control over the Database Schema, table names, and column names. Because of this, our table and column names have no obvious meaning to them.
We are currently seeding data with the schema.sql and data.sql files in the resources directory. This hasn't been working for our team, because of the effort to seed the data with these obscure table and column makes. We often end up looking through our QA server for an account, then writing our code against a QA database.
My question:
How do I keep the schema.sql and data.sql files, but enable our team to seed data to the H2 database by using a seeder made available by JPA or Spring Data JPA.
I found a few examples of using JPA to seed data, but they don't mention where the files should be stored or how the files get called by the Spring Boot app on start up.
Our application only pulls data, and never inserts, so will I have to override the save function from the JpaRepository in order to accomplish this? Or can I just create an entity, and call the JpaRepository's save function?
Here is an obfuscated example of our account entity and repository:
AccountEntity.java
#Data
#Entity
#Table(name = "Table_Name")
#SecondaryTables({
#SecondaryTable(name = "Table_Name2", pkJoinColumns = {
#PrimaryKeyJoinColumn(name = "ACCT_ID", referencedColumnName = "ACCT_ID") }),
#SecondaryTable(name = "Table_Name3", pkJoinColumns = {
#PrimaryKeyJoinColumn(name = "ACCT_ID", referencedColumnName = "ACCT_ID") }),
#SecondaryTable(name = "Table_Name4", pkJoinColumns = {
#PrimaryKeyJoinColumn(name = "ACCT_ID", referencedColumnName = "ACCT_ID") })
})
public class AccountEntity {
#Column(name = "ACCT_ID")
#Id
private Integer accountIdNumber;
#Column(name = "SOME_OTHER_COLUMN1")
private String someOtherColum1;
#Column(name = "SOME_OTHER_COLUMN2", table = "Table_Name3")
private String someOtherColum2;
#Column(name = "SOME_OTHER_COLUMN3", table = "Table_Name4")
private Integer someOtherColum3;
...
}
AccountRepository.java
#Repository
public interface AccountRepository extends JpaRepository<AccountEntity, Integer> {
public AccountEntity findByAccountIdNumber(Integer accountNumber);
public List<AccountEntity> findAllByProp1Prop2AndProp3(
String prop1, String prop2, String prop3);
}

Resources