AfterConvertCallback dont call on custom methode repository - spring

i need a little help on my spring r2dbc project, i have a r2dbc repository who look like this :
#Repository
interface CurrencyAccountRepository : ReactiveSortingRepository<CurrencyAccount, String> {
fun findAllBy(page: Pageable): Flux<CurrencyAccount>
fun findByName(name: String): Mono<CurrencyAccount>
}
i have make some "custom" function on repository
i make a AfterConvertCallback for make my join on other table :
#Component
class CurrencyAccountCallback(#Lazy val accountRepository: AccountRepository) : AfterConvertCallback<CurrencyAccount> {
override fun onAfterConvert(entity: CurrencyAccount, table: SqlIdentifier): Publisher<CurrencyAccount> {
return just(entity).flatMap { linkAccount(it) }
}
private fun linkAccount(currencyAccount: CurrencyAccount): Mono<CurrencyAccount> {
return accountRepository.findById(currencyAccount.id)
.map { currencyAccount.setAccount(it) }
.switchIfEmpty(just(currencyAccount))
}
}
That's work when i use gived function from ReactiveSortingRepository like findById or findAll, that's call the AfterConvertCallback and that make the join.
But when i try to use the findAllBy or findByName the AfterConvertCallback is never call
Any one have an idea ? i think i miss an annotation but i dont find what

Related

Additional methods to the generated Panache REST Data resource using repository pattern

I'm using Quarkus with Hibernate Reactive and Panache REST Data. I'm using the Repository pattern. I would like to use the PanacheRepositoryResource and add a custom endpoint that calls a method in the EntityRepository, but I don't know how to inject the entityRepository since it is an interface.
#ResourceProperties
public interface EntityResource extends PanacheRepositoryResource<EntityRepository, Entity, Long> {
#GET
#Path("/customMethod")
default Uni<List<Entity>> repositoryMethod() {
return entityRepository.customMethod(); // <-- How can I inject my repository?
}
}
Any ideas?
You can do something like this:
#ResourceProperties
public interface EntityResource extends PanacheRepositoryResource<EntityRepository, Entity, Long> {
#GET
#Path("/customMethod")
default Uni<List<Entity>> repositoryMethod() {
return CDI.current().select(EntityRepository.class).get().customMethod();
}
}

Is there a way to override JPA saveAll method to used customized UPSERT query?

I am trying to override the JpaRepository saveAll method to use the custom UPSERT query in java SpringBoot. Is it possible?
As it's only one repository you can create a custom repository like this. I assume that the Entity name is User:
Your interface with only this saveAll Method
interface CustomizedUserRepository {
void savAllWithUpsert(Iterator<User> entities);
}
Then you have to implement the interface
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
public void savAllWithUpsert(Iterator<User> entities) {
// Your custom implementation
}
}
The most important part of the class name that corresponds to the fragment interface is the Impl postfix.
And finally use but all together:
interface UserRepository extends JpaRepository<User, Long>, CustomizedUserRepository {
}
Please also read the full docuementaion: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.single-repository-behavior
I used JdbcTemplate (NamedParameterJdbcTemplate)
//Bean
#Bean
public NamedParameterJdbcTemplate namedParameterJdbcTemplate() {
JdbcTemplate template = new JdbcTemplate(hikariDataSource);
template.setQueryTimeout(Integer.parseInt(queryTimeout));
return new NamedParameterJdbcTemplate(template);
}
Then
//Autowire NamedParameterJdbcTemplate
MapSqlParameterSource[] paramsArray =
mapperClass.mapDTOstoSqlParameterSource(items);
namedParameterJdbcTemplate.batchUpdate(SQL_qUERY, paramsArray);
Then
//Mapper class
public static MapSqlParameterSource[]
mapDTOstoSqlParameterSource(List<ItemDTO> items) {
List<MapSqlParameterSource> params = new ArrayList<>();
for (ItemDTO obj : items) {
MapSqlParameterSource source = new MapSqlParameterSource();
source.addValue("queryPara1", obj.getID());
source.addValue("queryPara2", obj.getSomething());
params.add(source);
}
return params.toArray(MapSqlParameterSource[]::new);
}

Kotlin, Spring Boot, JPA - take value of a Generic Enum (E.valueOf(stringValue))

Background
I'm developing a Spring Boot application and I'm using Kotlin, IntelliJ and Gradle (Groovy). I have some enum class in my code and I need to persist them (with JPA). I used a simple global converter.
// Sample Enum
enum class Policy {
PUBLIC,
INVITE_ONLY
}
// Sample Converter
#Converter(autoApply = true)
class PolicyConverter : AttributeConverter<Policy, String> {
override fun convertToDatabaseColumn(attribute: Policy): String {
return attribute.name
}
override fun convertToEntityAttribute(dbData: String): Policy {
return Policy.valueOf(dbData.toUpperCase())
}
}
Problem
Since I have 5-6 enums and I hate duplicated code, I thought about a generic converter that should do the work for every given enum. I tried to code something, but nothing worked. This is what I was thinking about:
abstract class EnumConverter<E: Enum<E>> : AttributeConverter<E, String> {
override fun convertToDatabaseColumn(attribute: E): String {
return attribute.name
}
override fun convertToEntityAttribute(dbData: String): E {
return E.valueOf(dbData.toUpperCase())
}
}
In this way I can only extend from one abstract class every enum converter, like so:
#Converter(autoApply = true)
class PolicyConverter : EnumConverter<Policy>() {}
Problem with this code is that I have two errors:
E is red because: Type parameter 'E' cannot have or inherit a companion object, so it cannot be on the left hand side of dot
valueOf is red because: unresolved reference (there are like 150+ types of .valueOf).
As suggested from this I tried to use following function:
private inline fun <reified E : Enum<E>> getValue(string: String): E {
return enumValueOf(string.toUpperCase())
}
But when called from the .convertToEntityAttribute, the result is that "Cannot use 'E' as reified type parameter. Use a class instead."
Question
So the question is simple: how can I implement an easy and fast way to make one converter for all my enums, that all follows the same principle? I just need a return E.valueOf(<value>) function, but it's not present.
A simply workaround of this problem is to define an abstract method that every class will implement and it will return the correct type, given a string.
// Inside EnumConverter, the Generic Class
abstract class EnumConverter<E: Enum<E>> : AttributeConverter<E, String> {
abstract fun getValueFromString(string: String) : E
override fun convertToEntityAttribute(dbData: String): E {
return getValueFromString(dbData)
}
[...]
}
// Inside Policy Enum, implementing
class PolicyConverter : EnumConverter<Policy>() {
override fun getValueFromString(string: String): Policy {
return Policy.valueOf(string.toUpperCase())
}
}
But it's a workaround that I really dislike.

How to hook all successfull inserts, updates and deletions in a spring data repository

I am currently trying to build hooks that get a list of all successfully commited entity changes of a repository:
#Entity data class User(#Id val id: Long, val name: String)
interface extends JpaRepository<User, Long>
#Service
class UserService(val userRepository:UserRepository){
#Transactional
fun someProcess(){
val newUser = User()
newUser.name = "newUser"
userRepository.save(newUser)
val userToUpdate = userRepository.findById(1).get()
userToUpdate.name = "updatedUser"
val userToDelete = userRepository.findById(2).get()
userRepository.delete(userToDelete)
}
// TODO implement hooks
fun hookAllInserts(inserts :List<User>){
// list is expected to contain newUser
}
fun hookAllUpdates(updates: List<User>) {
// list is expected to contain the latest state of updatedUser
}
fun hookAllDeletions(deletions: List<User>){
// list is expected to contain the deletedUser
}
}
The hooks should only be triggered after a successful commit, so changes that are rolled back are not propagated.
How can I achieve this with spring data?
I have not found a general solution for all spring data repositories,
however there is a solution for when one uses spring data in conjunction with hibernate:
#Component
class ChangeListener(
private val entityManagerFactory: EntityManagerFactory,
) : PostUpdateEventListener, PostInsertEventListener, PostDeleteEventListener {
#PostConstruct
private fun init() {
val sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl::class.java)
val registry = sessionFactory.serviceRegistry.getService(EventListenerRegistry::class.java)
registry.getEventListenerGroup(EventType.POST_COMMIT_UPDATE).appendListener(this)
registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener(this)
registry.getEventListenerGroup(EventType.POST_COMMIT_DELETE).appendListener(this)
}
override fun requiresPostCommitHanding(persister: EntityPersister): Boolean {
return true
}
override fun onPostUpdate(event: PostUpdateEvent) {
// event.entity contains the latest state of the entity
}
override fun onPostDelete(event: PostDeleteEvent) {
// event.entity contains the latest state of the entity
}
override fun onPostInsert(event: PostInsertEvent) {
// event.entity contains the last state of the entity
}
}
It is based on Hibernate EventListeners
See also here.
There is also a nice post on Vlad Mihalcea's blog

How to use Spring Data JPA methods returning a Stream in a try-with-resources block in Kotlin?

So I want to create a Spring Boot with Spring Data JPA project using Kotlin and lets say I have a Person entity. Lets say like this:
#Entity
public class Person {
private #GeneratedValue #Id Long id;
private String name;
#OneToMany
private List<Person> friends;
…
}
I would create the following interface to be able to use Try-with-Resources and a Stream<Person>.
public interface PersonRepository extends Repository<Person, Long> {
#Query("select p from Person p")
Stream<Person> findAllStream();
}
So normally in my service I would do this:
#Service
class MyService {
#Autowired PersonRepository repository;
List<String> foo() {
try(Stream<Person> stream = repository.findAllStream()) {
return stream.flatMap(p -> p.getFriends().stream())
.map(f -> f.getName())
.collect(Collectors.toList());
}
}
}
Now if you want to do this in Kotlin (The IntelliJ converter doesn't produce valid code). I suppose you would normally do something like:
class MyService #Autowired constructor(val personRepository: PersonRepository) {
fun foo() {
val list = personRepository.findAllStream()
.use {{p -> p.friends.stream()}.map {f -> f.name}}
}
}
Only you cant do that since there is no #use method on stream and you cant call #stream() from a List. So is there any way to do this?
Well, Java 8 support is not yet complete in Kotlin. So you can just declare use on your side like this
inline fun <A : AutoCloseable, R> A.use(block: (A) -> R): R {
try {
return block(this)
} finally {
close()
}
}
The other alternative is to declare it directly on Stream
inline fun <T, R> Stream<T>.use(block: (Stream<T>) -> R): R {
try {
return block(this)
} finally {
close()
}
}
UPD
If you are new to Kotlin you have to notice that extensions are resolved statically:
Extensions do not actually modify classes they extend. By defining an extension, you do not insert new members into a class, but merely make new functions callable with the dot-notation on instances of this class.
See more http://kotlinlang.org/docs/reference/extensions.html#extensions-are-resolved-statically

Resources