mirror of
https://github.com/corda/corda.git
synced 2025-04-07 11:27:01 +00:00
[CORDA-2247]: Fixed a bug causing Hibernate to produce cross joins. (#4366)
This commit is contained in:
parent
5ad992d01f
commit
ad48301149
@ -24,6 +24,8 @@ Unreleased
|
||||
|
||||
The ``confidential-identities`` dependency in your CorDapp must now be ``compile`` and not ``cordaCompile``.
|
||||
|
||||
* Fixed a bug resulting in poor vault query performance and incorrect results when sorting.
|
||||
|
||||
* Marked the ``Attachment`` interface as ``@DoNotImplement`` because it is not meant to be extended by CorDapp developers. If you have already
|
||||
done so, please get in contact on the usual communication channels.
|
||||
|
||||
|
@ -254,6 +254,8 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
|
||||
private val log = contextLogger()
|
||||
}
|
||||
|
||||
// incrementally build list of join predicates
|
||||
private val joinPredicates = mutableListOf<Predicate>()
|
||||
// incrementally build list of root entities (for later use in Sort parsing)
|
||||
private val rootEntities = mutableMapOf<Class<out StatePersistable>, Root<*>>(Pair(VaultSchemaV1.VaultStates::class.java, vaultStates))
|
||||
private val aggregateExpressions = mutableListOf<Expression<*>>()
|
||||
@ -566,7 +568,7 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
|
||||
else
|
||||
aggregateExpressions
|
||||
criteriaQuery.multiselect(selections)
|
||||
val combinedPredicates = commonPredicates.values.plus(predicateSet).plus(constraintPredicates)
|
||||
val combinedPredicates = commonPredicates.values.plus(predicateSet).plus(constraintPredicates).plus(joinPredicates)
|
||||
criteriaQuery.where(*combinedPredicates.toTypedArray())
|
||||
|
||||
return predicateSet
|
||||
@ -659,7 +661,12 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
|
||||
|
||||
val orderCriteria = mutableListOf<Order>()
|
||||
|
||||
sorting.columns.map { (sortAttribute, direction) ->
|
||||
val actualSorting = if (sorting.columns.none { it.sortAttribute == SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF) }) {
|
||||
sorting.copy(columns = sorting.columns + Sort.SortColumn(SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF), Sort.Direction.ASC))
|
||||
} else {
|
||||
sorting
|
||||
}
|
||||
actualSorting.columns.map { (sortAttribute, direction) ->
|
||||
val (entityStateClass, entityStateAttributeParent, entityStateAttributeChild) =
|
||||
when (sortAttribute) {
|
||||
is SortAttribute.Standard -> parse(sortAttribute.attribute)
|
||||
@ -670,6 +677,8 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
|
||||
// scenario where sorting on attributes not parsed as criteria
|
||||
val entityRoot = criteriaQuery.from(entityStateClass)
|
||||
rootEntities[entityStateClass] = entityRoot
|
||||
val joinPredicate = criteriaBuilder.equal(vaultStates.get<PersistentStateRef>("stateRef"), entityRoot.get<PersistentStateRef>("stateRef"))
|
||||
joinPredicates.add(joinPredicate)
|
||||
entityRoot
|
||||
}
|
||||
when (direction) {
|
||||
@ -688,6 +697,7 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
|
||||
}
|
||||
if (orderCriteria.isNotEmpty()) {
|
||||
criteriaQuery.orderBy(orderCriteria)
|
||||
criteriaQuery.where(*joinPredicates.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,6 +333,88 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `query with sort criteria works even when multiple pages have the same value for the sort criteria field`() {
|
||||
val numberOfStates = 59
|
||||
val pageSize = 13
|
||||
|
||||
database.transaction {
|
||||
vaultFiller.fillWithSomeTestLinearStates(numberOfStates, linearNumber = 100L)
|
||||
}
|
||||
val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
||||
|
||||
val sortAttribute = SortAttribute.Custom(DummyLinearStateSchemaV1.PersistentDummyLinearState::class.java, "linearNumber")
|
||||
|
||||
Sort.Direction.values().forEach { sortDirection ->
|
||||
|
||||
val sorting = Sort(listOf(Sort.SortColumn(sortAttribute, sortDirection)))
|
||||
val allStates = vaultService.queryBy<DummyLinearContract.State>(sorting = sorting, criteria = criteria).states
|
||||
assertThat(allStates.groupBy(StateAndRef<*>::ref)).hasSameSizeAs(allStates)
|
||||
when (sortDirection) {
|
||||
Sort.Direction.ASC -> assertThat(allStates.sortedBy { it.state.data.linearNumber }.sortedBy { it.ref.txhash }.sortedBy { it.ref.index }).isEqualTo(allStates)
|
||||
Sort.Direction.DESC -> assertThat(allStates.sortedByDescending { it.state.data.linearNumber }.sortedBy { it.ref.txhash }.sortedBy { it.ref.index }).isEqualTo(allStates)
|
||||
}
|
||||
|
||||
(1..3).forEach {
|
||||
val newAllStates = vaultService.queryBy<DummyLinearContract.State>(sorting = sorting, criteria = criteria).states
|
||||
assertThat(newAllStates.groupBy(StateAndRef<*>::ref)).hasSameSizeAs(allStates)
|
||||
assertThat(newAllStates).containsExactlyElementsOf(allStates)
|
||||
}
|
||||
|
||||
val queriedStates = mutableListOf<StateAndRef<*>>()
|
||||
var pageNumber = 0
|
||||
while (pageNumber * pageSize < numberOfStates) {
|
||||
val paging = PageSpecification(pageNumber = pageNumber + 1, pageSize = pageSize)
|
||||
val page = vaultService.queryBy<DummyLinearContract.State>(sorting = sorting, paging = paging, criteria = criteria)
|
||||
queriedStates += page.states
|
||||
pageNumber++
|
||||
}
|
||||
|
||||
assertThat(queriedStates).containsExactlyElementsOf(allStates)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `query with sort criteria works with pagination`() {
|
||||
val numberOfStates = 59
|
||||
val pageSize = 13
|
||||
|
||||
database.transaction {
|
||||
vaultFiller.fillWithSomeTestLinearStates(numberOfStates, linearNumber = 100L)
|
||||
}
|
||||
val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
||||
|
||||
val sortAttribute = SortAttribute.Custom(DummyLinearStateSchemaV1.PersistentDummyLinearState::class.java, "stateRef")
|
||||
|
||||
Sort.Direction.values().forEach { sortDirection ->
|
||||
|
||||
val sorting = Sort(listOf(Sort.SortColumn(sortAttribute, sortDirection)))
|
||||
val allStates = vaultService.queryBy<DummyLinearContract.State>(sorting = sorting, criteria = criteria).states
|
||||
assertThat(allStates.groupBy(StateAndRef<*>::ref)).hasSameSizeAs(allStates)
|
||||
when (sortDirection) {
|
||||
Sort.Direction.ASC -> assertThat(allStates.sortedBy { it.ref.txhash }.sortedBy { it.ref.index }).isEqualTo(allStates)
|
||||
Sort.Direction.DESC -> assertThat(allStates.sortedByDescending { it.ref.txhash }.sortedByDescending { it.ref.index }).isEqualTo(allStates)
|
||||
}
|
||||
|
||||
(1..3).forEach {
|
||||
val newAllStates = vaultService.queryBy<DummyLinearContract.State>(sorting = sorting, criteria = criteria).states
|
||||
assertThat(newAllStates.groupBy(StateAndRef<*>::ref)).hasSameSizeAs(allStates)
|
||||
assertThat(newAllStates).containsExactlyElementsOf(allStates)
|
||||
}
|
||||
|
||||
val queriedStates = mutableListOf<StateAndRef<*>>()
|
||||
var pageNumber = 0
|
||||
while (pageNumber * pageSize < numberOfStates) {
|
||||
val paging = PageSpecification(pageNumber = pageNumber + 1, pageSize = pageSize)
|
||||
val page = vaultService.queryBy<DummyLinearContract.State>(sorting = sorting, paging = paging, criteria = criteria)
|
||||
queriedStates += page.states
|
||||
pageNumber++
|
||||
}
|
||||
|
||||
assertThat(queriedStates).containsExactlyElementsOf(allStates)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed states with count`() {
|
||||
database.transaction {
|
||||
|
Loading…
x
Reference in New Issue
Block a user