์๋ ํ์ธ์, ํ์ด์ง ๋๋ ๋ฒ์๋ฅผ ์ฌ์ฉํ ๋ 1.0.0.CR9์์ MS SQL ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค(SQL ์์ธ, message=''?' ๊ทผ์ฒ์ ์๋ชป๋ ๊ตฌ๋ฌธ.'). ๋งค๊ฐ๋ณ์๊ฐ Select ์ ์ ์๊ธฐ ๋๋ฌธ์ผ ์ ์์ต๋๊น?
์คํ: Quarkus 2.1.0.Final / Panache Reactive / Mutiny 4.1.2 / Hibernate Reactive 1.0.0.CR9 / Hibernate Core 5.5.5.Final
๋ด ๋๋จธ์ง ๋ฆฌ์์ค์์ ๋ค์์ ์คํํฉ๋๋ค.
[PanacheRepository].find("firstName LIKE ?1").page(Page.of(25)).list();
Hibernate์์ SQL ๋ก๊ทธ์จ์ผ๋ก ์์ฑ๋ SQL์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
Hibernate:
select
top(?) request0_.id as id1_1_,
request0_.caseId as caseid2_1_,
request0_.caseNumber as casenumb3_1_,
request0_.expires as expires4_1_,
request0_.addressLine1 as addressl5_1_,
request0_.addressLine2 as addressl6_1_,
request0_.agency as agency7_1_,
request0_.city as city8_1_,
request0_.email as email9_1_,
request0_.firstName as firstna10_1_,
request0_.lastName as lastnam11_1_,
request0_.phone as phone12_1_,
request0_.relationship as relatio13_1_,
request0_.state as state14_1_,
request0_.zipcode as zipcode15_1_,
request0_.submitted as submitt16_1_
from
Request request0_
where
request0_.firstName like <strong i="11">@P1</strong>
where ์ ์ ์ฌ๋ฐ๋ฅธ ๋งค๊ฐ๋ณ์ ๋์ฒด์ ์ ์ํ์ญ์์ค. ๊ทธ๋ฌ๋ select ์ ์ "top(?)"์ ๋ํ ๋์ฒด๊ฐ ๋ฐ์ํ์ง ์์์ต๋๋ค.
ํ์ด์ง์ ์ ๊ฑฐํ๊ณ ๋ค์ ์คํํ๋ฉด ์ฟผ๋ฆฌ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์คํ๋ฉ๋๋ค.
HQL ๋ฐ SQL์ ๋ณด์ฌ์ฃผ๋ ์๋ ํ์๋ ์ถ์ ๋ก๊น
. Panache๊ฐ ์ฟผ๋ฆฌ์ setMaxResults()
๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๊ณ ์ถ์ธกํฉ๋๋ค. Quarkus ๋๋ Mutiny์ ๋ฌธ์ ๊ฐ ์๋ ๊ฒฝ์ฐ ๋ฌธ์ ๋ฅผ ์ด๊ฒ ๋์ด ๊ธฐ์ฉ๋๋ค. ๋ฏธ๋ฆฌ ๊ฐ์ฌ๋๋ฆฝ๋๋ค!
2021-08-05 12:55:57,342 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-1) HQL: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1
2021-08-05 12:55:57,342 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-1) SQL: select request0_.id as id1_1_, request0_.caseId as caseid2_1_, request0_.caseNumber as casenumb3_1_, request0_.expires as expires4_1_, request0_.addressLine1 as addressl5_1_, request0_.addressLine2 as addressl6_1_, request0_.agency as agency7_1_, request0_.city as city8_1_, request0_.email as email9_1_, request0_.firstName as firstna10_1_, request0_.lastName as lastnam11_1_, request0_.phone as phone12_1_, request0_.relationship as relatio13_1_, request0_.state as state14_1_, request0_.zipcode as zipcode15_1_, request0_.submitted as submitt16_1_ from Request request0_ where request0_.firstName like ?
2021-08-05 12:55:57,342 DEBUG [org.hib.hql.int.ast.ErrorTracker] (vert.x-eventloop-thread-1) throwQueryException() : no errors
2021-08-05 12:55:57,342 TRACE [org.hib.rea.ses.imp.ReactiveHQLQueryPlan] (vert.x-eventloop-thread-1) Find: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1
2021-08-05 12:55:57,344 TRACE [org.hib.eng.spi.QueryParameters] (vert.x-eventloop-thread-1) Named parameters: {1=%j%}
2021-08-05 12:55:57,345 TRACE [org.hib.typ.des.sql.BasicBinder] (vert.x-eventloop-thread-1) binding parameter [2] as [VARCHAR] - [%j%]
2021-08-05 12:55:57,345 TRACE [org.hib.loa.Loader] (vert.x-eventloop-thread-1) Bound [3] parameters total
2021-08-05 12:55:57,355 DEBUG [org.hib.SQL] (vert.x-eventloop-thread-1)
select
top(?) request0_.id as id1_1_,
request0_.caseId as caseid2_1_,
request0_.caseNumber as casenumb3_1_,
request0_.expires as expires4_1_,
request0_.addressLine1 as addressl5_1_,
request0_.addressLine2 as addressl6_1_,
request0_.agency as agency7_1_,
request0_.city as city8_1_,
request0_.email as email9_1_,
request0_.firstName as firstna10_1_,
request0_.lastName as lastnam11_1_,
request0_.phone as phone12_1_,
request0_.relationship as relatio13_1_,
request0_.state as state14_1_,
request0_.zipcode as zipcode15_1_,
request0_.submitted as submitt16_1_
from
Request request0_
where
request0_.firstName like <strong i="18">@P1</strong>
2021-08-05 12:55:57,358 ERROR [org.hib.rea.errors] (vert.x-eventloop-thread-1) HR000057: Failed to execute statement [$1select request0_.id as id1_1_, request0_.caseId as caseid2_1_, request0_.caseNumber as casenumb3_1_, request0_.expires as expires4_1_, request0_.addressLine1 as addressl5_1_, request0_.addressLine2 as addressl6_1_, request0_.agency as agency7_1_, request0_.city as city8_1_, request0_.email as email9_1_, request0_.firstName as firstna10_1_, request0_.lastName as lastnam11_1_, request0_.phone as phone12_1_, request0_.relationship as relatio13_1_, request0_.state as state14_1_, request0_.zipcode as zipcode15_1_, request0_.submitted as submitt16_1_ from Request request0_ where request0_.firstName like @P1]: $2could not execute query: java.util.concurrent.CompletionException: io.vertx.mssqlclient.MSSQLException: {number=102, state=1, severity=15, message='Incorrect syntax near '?'.', serverName='aa3ae4a0fd3c', lineNumber=1, additional=[io.vertx.mssqlclient.MSSQLException: {number=8180, state=1, severity=16, message='Statement(s) could not be prepared.', serverName='aa3ae4a0fd3c', lineNumber=1}]}
SQL์ ์ด์ํ ์ ์ด ์์ต๋๋ค.
์ ์ฐ๋ฆฌ๋ ์ฌ์ง์ด ์ฌ์ฉํ๋ top n
๋์ offset ... fetch ...
. ๊ทธ๋ฆฌ๊ณ ์ ์ค๋ณต ๊ดํธ๊ฐ ํ์ํฉ๋๊น?
Panache๊ฐ ์ฟผ๋ฆฌ์
setMaxResults()
๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๊ณ ์ถ์ธกํฉ๋๋ค.
๊ธ์, ๊ทธ๊ฒ์ ํ์คํ ํด์ผ ํฉ๋๋ค. Panache๋ ์์ฒด์ ์ธ SQL ์์ฑ์ ํ๊ณ ์์ง ์์ต๋๋ค. @FroMage?
๊ธ์, ๋๋ ์ด๊ฒ์ ์ผ๋ฐ HR์์ ์๋ํ๊ณ ๋๋ ๋ค์์ ์ป๋๋ค.
select hqlquerypa0_.id as id1_0_, hqlquerypa0_.description as descript2_0_, hqlquerypa0_.name as name3_0_, hqlquerypa0_.type as type4_0_ from Flour hqlquerypa0_ order by hqlquerypa0_.id offset <strong i="6">@P1</strong> rows fetch next <strong i="7">@P2</strong> rows only
๊ทธ๋์ ์ด๊ฒ์ Panache์ ๊ด๋ จ๋ ๋ฌธ์ ์์ ํ๋ฆผ์๋ค๊ณ ์๊ฐ๋ฉ๋๋ค.
๊ฒํ ํด ์ฃผ์
์ ๊ฐ์ฌํฉ๋๋ค. ์คํ์ ์๋ ์ผ๋ถ ํ๋ก์ ํธ์ ์ฐ๋ฝํ๊ฒ ์ต๋๋ค. ์ถ์ ๋ก๊ทธ์ ํ์๋ ์ ์ผํ HQL ๋๋ถํฐ FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1
, ๋๋์ ์ถ๊ฐ ๊ฐ์ top(?)
์ ๋ํ MS SQL ๊ตฌํ์์์ค๊ณ setMaxResults()
๊ฒ์๋ฌผ์, ๊ทธ๋ฌ๋ SQL์ ์์ ํ ๋ค๋ฅธ ๊ตฌ๋ฌธ์ ์ฌ์ฉํ๊ณ ์์์ ๋ณด์ฌ์ค๋๋ค.
์ฌ์ ํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ฌ ๋ค์ HQL์ ์ฌ์ฉํ๋๋ก ์ฃผ๋ฌธ์ ์ถ๊ฐํ๋ ค๊ณ ์๋ํ์ต๋๋ค. FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 ORDER BY submitted
2021-08-05 16:20:37,992 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-19) HQL: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 ORDER BY submitted
2021-08-05 16:20:37,992 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-19) SQL: select request0_.id as id1_1_, request0_.caseId as caseid2_1_, request0_.caseNumber as casenumb3_1_, request0_.expires as expires4_1_, request0_.addressLine1 as addressl5_1_, request0_.addressLine2 as addressl6_1_, request0_.agency as agency7_1_, request0_.city as city8_1_, request0_.email as email9_1_, request0_.firstName as firstna10_1_, request0_.lastName as lastnam11_1_, request0_.phone as phone12_1_, request0_.relationship as relatio13_1_, request0_.state as state14_1_, request0_.zipcode as zipcode15_1_, request0_.submitted as submitt16_1_ from Request request0_ where request0_.firstName like ? order by request0_.submitted
์ด๊ฒ์ ๋ค์ SQL์ ์คํํ๋ ค๊ณ ์๋ํ์ต๋๋ค.
2021-08-05 16:20:38,022 DEBUG [org.hib.SQL] (vert.x-eventloop-thread-19)
select
request0_.id as id1_1_,
request0_.caseId as caseid2_1_,
request0_.caseNumber as casenumb3_1_,
request0_.expires as expires4_1_,
request0_.addressLine1 as addressl5_1_,
request0_.addressLine2 as addressl6_1_,
request0_.agency as agency7_1_,
request0_.city as city8_1_,
request0_.email as email9_1_,
request0_.firstName as firstna10_1_,
request0_.lastName as lastnam11_1_,
request0_.phone as phone12_1_,
request0_.relationship as relatio13_1_,
request0_.state as state14_1_,
request0_.zipcode as zipcode15_1_,
request0_.submitted as submitt16_1_
from
Request request0_
where
request0_.firstName like <strong i="10">@P1</strong>
order by
request0_.submitted offset 0 rows fetch next ? rows only
๊ทธ๋์ ... ๋งค๊ฐ ๋ณ์ ๋์ฒด๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ํํ์ง ์์๋ค๋ ์ ์์ ๋์ผํ ๋ฌธ์ ๊ฐ ์ฌ์ ํ ์กด์ฌํ์ง๋ง (ํ์ฌ fetch next ? rows only
) ์์ฑ ๋ SQL ๊ตฌ๋ฌธ์ด ์์ ์ ์ผ์นํ๋๋ก ๋ณ๊ฒฝ๋์์ต๋๋ค.
Quarkus์๋ ์์ง hibernate-reactive-core 1.0.0.CR9๊ฐ ํฌํจ๋์ด ์์ง ์๊ธฐ ๋๋ฌธ์ ์ด ๋ฒ์ ๊ณผ hibernate-core 5.5.5.Final์ ๋ํ ๋ด pom์ ๋ฒ์ ์ ํน๋ณํ ๋ฎ์ด์ผ์ต๋๋ค. ์์กด์ฑ์ ๋์น ์ ์์ต๋๊น?
๋๋ Hibernate ์ฟผ๋ฆฌ๊ฐ Panache io.quarkus.hibernate.reactive.panache.common.runtime.CommonPanache QueryImpl:L254 ์์ ์์ฑ๋์๋ค๊ณ ๋ฏฟ๋ ๊ณณ์ ๋ณด์๊ณ setFirstResult
๋ฐ setMaxResults
๋ง ์ฌ์ฉํ๋ ๊ฒ์ผ๋ก ๋ณด์
๋๋ค. ์ฟผ๋ฆฌ์. ๋๋ฒ๊ฑฐ์์ ์ฟผ๋ฆฌ ๊ฐ์ฒด๋ฅผ ๋ณด์๊ณ ํด๋น createQuery
๋ฉ์๋์ ๋์์ ๊ธฐ๋ณธ ReactiveQueryImpl์๋ firstRow=0 ๋ฐ maxRows=25๋ฅผ ํฌํจํ๋ queryOptions๊ฐ ์์ต๋๋ค.
๋์์ ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค! (๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ์ฌ์ฉํ๊ณ ์์์ ์ดํด)
SQLServerParameters.processLimit() ์์ ๋ฒ๊ทธ๋ฅผ ์ฐพ์ ๊ฒ ๊ฐ์ต๋๋ค.
Panache๋ firstRow=0์ธ ์ต๋ ์ ์ ๋ชจ๋ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๊ณ ์์ต๋๋ค.
org.hibernate.reactive.loader.ReactiveLoader.executeReactiveQueryStatement
๋ด๋ถ์์ SQLServer2012LimitHandler.getOffsetFetch()
๊ฐ ํธ์ถ๋ฉ๋๋ค.
if ( !LimitHelper.hasFirstRow( selection ) ) {
return " offset 0 rows fetch next ? rows only";
}
return " offset ? rows fetch next ? rows only";
๋ฐ๋ผ์ ์ด ์์ ์ SQL์ offset 0 rows fetch next ? rows only
๋๋ฉ๋๋ค. ? ๋์ 0์ ์ ์ํ์ญ์์ค.
SQLServerParameters.processLimit()
:
if (isProcessingNotRequired(sql)) {
return sql;
}
// Replace 'offset ? fetch next ? rows only' with the <strong i="17">@P</strong> style parameters for Sql Server
int index = hasOffset ? parameterArray.length - 1 : parameterArray.length;
int pos = sql.indexOf( " offset ?" );
if ( pos > -1 ) {
String sqlProcessed = sql.substring( 0, pos ) + " offset @P" + index++ + " rows";
if ( sql.indexOf( " fetch next ?" ) > -1 ) {
sqlProcessed += " fetch next @P" + index + " rows only ";
}
return sqlProcessed;
}
return sql;
sql.indexOf( " offset ?")
๊ฐ ๋ฌธ์ ์
๋๋ค. LimitHandler๊ฐ ์ด๋ฏธ ์คํ์
์ 0์ผ๋ก ํ๋ ์ฝ๋ฉํ๊ธฐ ๋๋ฌธ์ ํ์ ๋ฌธ์์ด offset ?
์ฐพ์ ์ ์์ผ๋ฏ๋ก ์ถ๊ฐ ๋งค๊ฐ๋ณ์๋ฅผ ์ฒ๋ฆฌํ๋ ค๊ณ ์๋ํ์ง ์์ต๋๋ค.
fetch next ?
์ฐพ๋ ์ค์ฒฉ ๋ฌธ์์ด ์ผ์น๋ฅผ offset
์ ๋ํ ์ผ์น ์ธ๋ถ๋ก ์ด๋ํด์ผ ํฉ๋๊น?
@bharward Hibernate Reactive๋ง์ ์ฌ์ฉํ๋ ํ ์คํธ ์ผ์ด์ค๋ฅผ ์์ฑํ ์ ์๋ค๋ฉด ๋งค์ฐ ๊ฐ์ฌํ ๊ฒ์ ๋๋ค.
Panache๋ฅผ ์ฌ์ฉํ๋ ์ ์ฅ์๋ ์ดํด๋ด์ผ๊ฒ ์ง๋ง ์ด ์ ์ฅ์๋ ๊ทธ๋ฐ ์ข ๋ฅ์ ๋ฌธ์ ์ ์ ํฉํ ์ฅ์๊ฐ ์๋๋๋ค.
์ฒซ ๋ฒ์งธ ๊ฒฐ๊ณผ๊ฐ ์๋ ์ต๋ ๊ฒฐ๊ณผ๋ง ์ค์ ํ๋ ํ ์คํธ๊ฐ ๋๋ฝ๋ ๊ฒ ๊ฐ์ต๋๋ค.
@gavinking ๋ฐ @DavidD - ๊ฐ์ฌํฉ๋๋ค. setFirstResult(0)
๋ฐ setMaxResults(n)
(n์ ์์์ ๊ฐ > 0) _๋ ๋ค ์ํํ๋ ๋ ๋ค๋ฅธ ํ
์คํธ๊ฐ ๋ด๊ฐ ๋ณด๊ณ ์๋ ์ค๋ฅ๋ฅผ ์ฌํํ ๊ฒ์ผ๋ก ๋ณด์
๋๋ค.
SQLServer2012LimitHandler.getOffsetFetch()
firstResult==0("offset 0 rows...")์ผ ๋์ firstResult >0("offset ? rows");
๊ทธ๋๋ ๋์์ด ๋๋ค๋ฉด ๋ช ์ผ ์์ ์ฌํํ ์์ HR ์ ์ฉ ์์ ๋ฅผ ๋ง๋ค๊ฑฐ๋ ๋ด๊ฐ ์ค๋ช ํ ๋๋ก HQLQueryParameterNamedLimitTest๋ฅผ ์์ ํ์ฌ ํ ์คํธ๋ฅผ ์ถ๊ฐํ๋ ๋ฐ ์๊ฐ์ ํ ์ ํ ์ ์์ต๋๋ค.
์, ๋ง์ต๋๋ค. ์ด ํ
์คํธ๋ HQLQueryParameterNamedLimitTest
์์ ์คํจํฉ๋๋ค.
<strong i="7">@Test</strong>
public void testFirstResultZeroAndMaxResults(TestContext context) {
test(
context,
openSession()
.thenCompose( s ->
s.createQuery( "from Flour order by id" )
.setFirstResult( 0 )
.setMaxResults( 10 )
.getResultList()
.thenAccept( result -> {
context.assertEquals( 3, result.size() );
} )
)
);
}
๋ด๊ฐ ๋ด์ค๊ฒ
@DavidD _์ถ๊ฐ๋ก_ ์ ์ํ ํ ์คํธ ์ธ์ ๋ค์๊ณผ ๊ฐ์ ์ถ๊ฐ ํ ์คํธ๋ ์ ์ํ ์ ์์ต๋๋ค.
<strong i="7">@Test</strong>
public void testFirstResultZeroAndMaxResultsWithoutOrderBy(TestContext context) {
test(
context,
openSession()
.thenCompose( s ->
s.createQuery( "from Flour" )
.setFirstResult( 0 )
.setMaxResults( 10 )
.getResultList()
.thenAccept( result -> {
context.assertEquals( 3, result.size() );
} )
)
);
}
์ฐจ์ด์ ์ "order by"๋ฅผ ์ ๊ฑฐํ๋ ๊ฒ์
๋๋ค. ๊ทธ ์ด์ ๋ ๊ฒ์
๋๋ค SQLServer2005LimitHandler์ด ๊ฐ์ ์ฟผ๋ฆฌ๋ฅผ ๊ธฐ๋ก select top(?) ...
์ฟผ๋ฆฌ๋ "์ ์ํด ์์"๋ฅผ ํฌํจํ์ง ์์ ๋๋ง๋ค,ํ์ง๋ง ๊ทธ๋ ๊ฒ๋๋ฉด ๊ทธ๊ฒ์ ์ฌ์ฉ offset ? fetch next ?
๊ตฌ๋ฌธ์. ๊ทธ๋์ 2๊ฐ์ ํ
์คํธ ์ผ์ด์ค๊ฐ ํ์ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๋งค์ฐ ๊ฐ์ฌ!!