Why JpaSpecificationExecutor doubles some HSQL queries? - spring-data-jpa

I have following code
#Transactional(readOnly = true)
override fun getProjects(
pageNum: Int,
pageSize: Int,
sortBy: List<com.fmetric.project_mgmt_api.util.Sort>,
filter: ProjectFilterDescriptor,
full: Boolean
): PageDescriptor<ProjectDescriptor> {
val orders = mutableListOf<Sort.Order>()
sortBy.forEach { sort -> orders.add(Sort.Order(map(sort.sortOrder), sort.sortBy)) }
val sort = Sort.by(orders)
val page = PageRequest.of(pageNum, pageSize, sort)
val spec = buildSpecification(filter)
val projectsPage = projectRepository.findAll(spec, page)
return Page(
projectsPage.number,
projectsPage.size,
projectsPage.content.map { p -> map(p, full) },
projectsPage.totalElements,
map(projectsPage.sort)
)
}
...
interface ProjectRepository : JpaSpecRepository<Project>, PagingAndSortingRepository<Project, UUID?> {
...
#PreAuthorize("addPermissionFilter(#spec, 'Project', 'View')")
override fun findAll(spec: Specification<Project>?, pageable: Pageable): Page<Project>
...
package org.springframework.data.jpa.repository
public interface JpaSpecificationExecutor<T> {
...
Page<T> findAll(#Nullable Specification<T> var1, Pageable var2);
...
it generates following hibernate log with six HSQL queries, two of them are duplicates, why?
https://gist.github.com/iva-nova-e-katerina/b336f34b666cbb69c26f682b378ca311
UPD: this is the classical "Hibernate N+1 problem" and the best working solution is described here https://medium.com/geekculture/resolve-hibernate-n-1-problem-f0e049e689ab . Problem is solved.

Related

How model a updatable view for a dynamic form builder and listen to their changes

I'm building a server-driven UI (aka: a form builder) that get the form definition dynamically in JSON, then renders that form.
I need to update the values and send back the results. Some fields also need to trigger validations, so I need to listen to them.
Doing this with JetPack is confusing (to me) because it is not clear how I architect the form builder.
The data for the form builder is:
#Serializable
enum class Action {
Create,
Update,
}
#Serializable
data class ErrorMsg(
val message: String,
val kind: ErrorKind
)
#Serializable
data class Form(
var main_action: Action?,
var title: String?,
var errors: ArrayList<ErrorMsg>
var sections: ArrayList<FormSection>,
)
// A form section is a group of fields
#Serializable
data class FormSection(
var id: UInt,
var fields: ArrayList<FormField>,
var title: String?
)
#Serializable
data class FormField(
val field: String,
val obligatory: Boolean,
val error: ErrorMsg?,
var value: String, //<-- This is what needs to trigger changes!
)
So, if I try to pass the form to a view:
#Composable
fun ComposablePreview() {
val f1 = UIData.FormField.txt("1", "Code", "001")
val f2 = UIData.FormField.pwd("2", "Pwd", "123")
val fields = arrayListOf(f1, f2)
val sections =
UIData.FormSection(
id = 0u, fields = fields, "Sample")
val form_base = UIData.Form(
main_action = null, sections= arrayListOf(sections))
val form by remember { mutableStateOf(form_base) }
FormView(form = form)
}
How get the changes propagated?
I build each form control alike
#Composable
fun TextCtrlView(
field: UIData.FormField,
) {
var value by remember { mutableStateOf(field.value) }
TextField(
label = { Text(field.label) },
value = value,
isError = field.error != null,
onValueChange = { value = it },
)
}

How to concatEagerDelayError in RxJava2

How to implement a Observable.concatEagerDelayError or an equivalent in RxJava2/RxKotlin2 ?
There is :
Observable.concatEager
Observable.concatDelayError
But not :
Observable.concatEagerDelayError
What i have :
fun getAll(): Observable<List<User>> = Observable.concatArrayDelayError(
// from db
userDAO
.selectAll()
.subscribeOn(ioScheduler),
// from api
userAPI
.getAll()
.doOnNext { lstUser -> Completable.concatArray(
userDAO.deleteAll().subscribeOn(ioScheduler),
userDAO.save(lstUser).subscribeOn(ioScheduler)
) }
.subscribeOn(ioScheduler)
)
I want same behaviour but eagerly for selectAll() and getAll() because there is no reason to wait from db to launch network call.
Use concatMapEagerDelayError:
Observable.fromIterable(sources)
.concatMapEagerDelayError(v -> v, true);
Observable.fromArray(source1, source2, source3)
.concatMapEagerDelayError(v -> v, true);
JavaDoc.
Edit:
fun getAll(): Observable<List<User>> = Observable.fromArray(
// from db
userDAO
.selectAll()
.subscribeOn(ioScheduler),
// from api
userAPI
.getAll()
// --- this makes no sense by the way -------------------
.doOnNext { lstUser -> Completable.concatArray(
userDAO.deleteAll().subscribeOn(ioScheduler),
userDAO.save(lstUser).subscribeOn(ioScheduler)
)}
// ------------------------------------------------------
.subscribeOn(ioScheduler)
)
.concatMapEagerDelayError({ v -> v }, true)

One to Many KStream-KTable join

I have a kStream of Universities -
when University is -
University(universityId: String, name: String, studentIds: Seq[String])
val universityKStream = builder.stream[String, University](...)
And a kTable of Students,
when Student is -
Student(studentId: String, name: String)
val studentsKtable = builder.table[String, Student](...)
I want to join the two and produce to a topic of ResolvedUniverity objects:
ResolvedUniversity(universityId: String, name: String, students: Seq[Student])
I cant groupBy and aggregate students with universityId, since universityId field doesn't exist in Student object..
Using just the DSL, I think the simplest you can do is (Java):
class Student {
String studentId;
String name;
}
class University {
String universityId;
String name;
List<String> studentIds;
}
class ResolvedUniversity {
String universityId;
String name;
List<Student> students;
}
Serde<String> stringSerde = null;
Serde<Student> studentSerde = null;
Serde<University> universitySerde = null;
Serde<ResolvedUniversity> resolvedUniversitySerde = null;
KStream<String, University> universities = topology
.stream("universities", Consumed.with(stringSerde, universitySerde));
KTable<String, Student> students = topology
.table("students", Consumed.with(stringSerde, studentSerde));
KTable<String, ResolvedUniversity> resolvedUniversities = universities
.flatMap((k, v) -> {
return v.studentIds.stream()
.map(id -> new KeyValue<>(id, v))
.collect(Collectors.toList());
})
.join(students, Pair::pair, Joined.with(stringSerde, universitySerde, studentSerde))
.groupBy((k, v) -> v.left().universityId)
.aggregate(ResolvedUniversity::new,
(k, v, a) -> {
a.universityId = v.left().universityId;
a.name = v.left().name;
a.students.add(v.right());
return a;
},
Materialized.with(stringSerde, resolvedUniversitySerde));
With this type of join, for historical processing your KTable of universities must be "primed" with its data before the KStream is joined against it.

How to ensure result count and caching with varying parameters

I have an API endpoint that can different result count based on request parameters. Parameters are page, per_page, query and others.
fun getItems(params : Map<String, String>) : Single<ItemsResponse>
data class ItemsResponse(
val hasMore : Boolean,
val items : List<Items>
)
API is not trustworthy and could return less than per_page. I want to ensure, that I always get result count I need and cache remainder for next request cycle.
For example something
val page : Int = 1
fun fetchItems(requestedItems : Int = 20) : Single<List<Items>> {
...
.map { buildParams(page, perPage, query) }
.flatMap { api.getItems(it) }
.doOnSuccess { page++ }
.buffer(requestedItems)
}
fun buildParams(page: Int, perPage: Int, query : String) : Map<String, String> {
...
}
Example scenario:
Caller requests 20 items for the first time.
Call to api.getItems() with page: 1, per_page is always 20.
Call returns 16 items
Call to api.getItems() with page: 2
Call return 19 items
20 items were returned to caller and 15 remaining items were cached for next caller request.
Caller requests 20 items for 2nd time.
Call to api.getItems() with page: 3
Call returns 12 items
20 items were returned to caller (15 older ones and 5 from last response) and 7 remaining items were cached for next caller requests.
And so on and so forth.
This looks like Producer-Consumer pattern, but is doable in RxJava2?
Edit: based on the additional info
Requires: RxJava 2 Extensions library: compile "com.github.akarnokd:rxjava2-extensions:0.17.0"
import hu.akarnokd.rxjava2.expr.StatementObservable
import io.reactivex.Observable
import io.reactivex.functions.BooleanSupplier
import io.reactivex.subjects.PublishSubject
import java.util.concurrent.Callable
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.ThreadLocalRandom
var counter = 0;
fun service() : Observable<String> {
return Observable.defer(Callable {
val n = ThreadLocalRandom.current().nextInt(21)
val c = counter++;
Observable.range(1, n).map({ v -> "" + c + " | " + v })
})
}
fun getPage(pageSignal : Observable<Int>, pageSize: Int) : Observable<List<String>> {
return Observable.defer(Callable {
val queue = ConcurrentLinkedQueue<String>()
pageSignal.concatMap({ _ ->
StatementObservable.whileDo(
service()
.toList()
.doOnSuccess({ v -> v.forEach { queue.offer(it) }})
.toObservable()
, BooleanSupplier { queue.size < pageSize })
.ignoreElements()
.andThen(
Observable.range(1, pageSize)
.concatMap({ _ ->
val o = queue.poll();
if (o == null) {
Observable.empty()
} else {
Observable.just(o)
}
})
.toList()
.toObservable()
)
})
})
}
fun main(args: Array<String>) {
val pages = PublishSubject.create<Int>();
getPage(pages, 20)
.subscribe({ println(it) }, { it.printStackTrace() })
pages.onNext(1)
pages.onNext(2)
}

Update Mongo document field from Grails app

I have a requirement where I have to check in the database if the value of a boolean variable(crawled) is false. If so I have to set it to true. I am finding the record based on the value of a string variable(website). I referenced this link but it didn't help. Please tell me what I am doing wrong.
I tried this:
def p = Website.findByWebsite(website);
if(p['crawled'] == true) {
println "already crawled"
} else {
mongo.website.update( p, new BasicDBObject( '$set', new BasicDBObject( 'crawled', 'false' ) ) )
println "updated object"
}
It gives me an error No such property: mongo for class: cmsprofiler.ResourceController
My domain class is as follows:
class Website{
String website
User user
Boolean crawled
static belongsTo = [user: User]
static constraints = {
website( url:true, unique: ['user'])
}
static hasMany = [resource: Resource]
static mapping = {resource cascade:"all-delete-orphan" }
}
you should be using
Website.mongo.update(...)
or let the framework inject it:
class ResourceController {
def mongo
def list(){
mongo.website.update(...)
}
}
This worked for me. Posting it here in case anyone else has similar requirement.
#Grab(group='com.gmongo', module='gmongo', version='0.9.3')
import com.gmongo.GMongo
#Transactional(readOnly = true)
class ResourceController {
def mongo = new GMongo()
def db = mongo.getDB("resources")
def p = Website.findByWebsite(website)
if(p['crawled']==false){
db.website.update([crawled:false],[$set:[crawled:true]])
}