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
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();
}
}
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);
}
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.
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
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