Hibernate3 提供了DetachedCriteria,使得我们可以在Web层构造 .=Pm>o/,
jEZ
"
detachedCriteria,然后调用业务层Bean,进行动态条件查询,根 &nQRa?3,
;US83%*
据这一功能,我设计了通用的抽象Bean基类和分页类支持,代码来 dKU5;
cICHRp&&
自于Quake Wang的javaeye-core包的相应类,然后又做了很多修改 S\B5&W
S&n[4*
。 q z=yMIy=
&c20x+
分页支持类: U8YO0}_z
Nt HbwU,
java代码: j,}4TDWa
[FB&4>V/
!\aV0,
package com.javaeye.common.util; rwoF}}
q1UBKhpnH
import java.util.List; 5+`=t07^et
}W1^t
publicclass PaginationSupport { /M 0 p_4
u/} xE7G
publicfinalstaticint PAGESIZE = 30; GUKDhg,W
wjuGq.qIu
privateint pageSize = PAGESIZE; e d_m +NM
!0b%Jh
privateList items; ?4:rP@
LxB&7
privateint totalCount; E\w+kAAf
w-lrnjs
privateint[] indexes = newint[0]; ^Ss<X}es-
!@( M_Z'
privateint startIndex = 0; 77``8,
6!Qknk$
public PaginationSupport(List items, int YQ52~M0L
o1U}/y+R\
totalCount){ ?F1wh2oq
setPageSize(PAGESIZE); "s% 686Vz
setTotalCount(totalCount); BjYOfu'~z
setItems(items); H;qJH1EdD
setStartIndex(0); )+?HI^-[S
} _ ~|Q4AJ
Y7-*2"!
public PaginationSupport(List items, int 4*iHw+%mq
9-b 8`|s
totalCount, int startIndex){ R^w}o,/
setPageSize(PAGESIZE); M]1;
setTotalCount(totalCount); 7iP5T
setItems(items); ?C}sR: K/
setStartIndex(startIndex); ^ZR8s^X
} O"qR }W
97!H`|u <
public PaginationSupport(List items, int R+s1[Z
$1~c_<DN
totalCount, int pageSize, int startIndex){ uw_H:-J
setPageSize(pageSize); =w6}\ 'X
setTotalCount(totalCount); L/)B}8m\
setItems(items); *y{+W
setStartIndex(startIndex); V+46R
]
} `6P?G|'
F,zG;_
publicList getItems(){ _1P`]+K\D$
return items; PzLJ/QER
} YN/u9[=`
lO[E[c G
publicvoid setItems(List items){ q4)Ey
this.items = items; GJvp{U}y9I
} n_J5zQJ
Jns/v6
publicint getPageSize(){ <z',]hy
return pageSize; +ZX.1[O
} Y3<b~!f
X CzXS.
publicvoid setPageSize(int pageSize){ AO $Wy@
this.pageSize = pageSize; kB
V/rw
} >{b3>s~T
};^}2Xo+
publicint getTotalCount(){ nW11wtiO.
return totalCount; g**5z'7
} ^Wm*-4
N2T&,&,t
publicvoid setTotalCount(int totalCount){ YIO.yN"0
if(totalCount > 0){ '^DUq?E4
this.totalCount = totalCount; >4~#%&
int count = totalCount / W1hX?!xp!
<}cZi4l'
pageSize; $D}"k!H
if(totalCount % pageSize > 0) G~(&3
count++; aV#h5s
indexes = newint[count]; _\UIc;3Gl
for(int i = 0; i < count; i++){ l77'Lne
indexes = pageSize * r,0@~;zA
8A!'I<S1
i; 2Y$
} :kt/$S^-
}else{ Iqx84
this.totalCount = 0; L/%Y#
} )O&z5n7t4s
} @gEr+O1K(
xvB8YW"
publicint[] getIndexes(){ {l@WCR
return indexes; n_}aZB3;U
} %XR<isn
~TM>"eB b
publicvoid setIndexes(int[] indexes){ -zdmr"CA
this.indexes = indexes; PV(4$I}
} 5/,Qz>QE[
_-RyHgX
publicint getStartIndex(){ 8RU.}PD
return startIndex; =gs~\q
} `|,Bm|~:
{pC\\}
publicvoid setStartIndex(int startIndex){ g8'~e{=(
if(totalCount <= 0) 3
1k
this.startIndex = 0; >4M<W4
elseif(startIndex >= totalCount) >MPa38
this.startIndex = indexes *{4
ETr7
bJPJ.+G7
[indexes.length - 1]; 6#vI;d[^
elseif(startIndex < 0) `
jyKCm.$#
this.startIndex = 0; &//2eL
else{ ;t!9]1
this.startIndex = indexes >8(jW
'B,KFA<
[startIndex / pageSize]; {"t5\U6cKM
} \FXp*FbQ
} ~?d>fR:X
;Yv14{T!
publicint getNextIndex(){ hJLT!33:
int nextIndex = getStartIndex() + Qh8C,"a
UBIIo'u
pageSize; 1fR P1
if(nextIndex >= totalCount) )(]Envb?A0
return getStartIndex(); `,P
>mp)uU
else N8QH*FX/F1
return nextIndex; TaWaHf
} -x5F;d}
|Qr:!MA
publicint getPreviousIndex(){ }jiK3?e
int previousIndex = getStartIndex() - 6bUl>4
bS%C?8
pageSize; tpGCrn2w>
if(previousIndex < 0) %I0}4$
return0; v^TkDf(Oz
else 7D9]R#-K
return previousIndex; ]Zk}ZG>6
}
QAUykS8
o} {-j
} =ajLa/m'
"&<~UiI
&(7$&Q
0qR$J
抽象业务类 59Nd}wPO;
java代码: \447]<u
8)?_{
4DM*^=9E
/** d- kZt@DL=
* Created on 2005-7-12 OpUA{P
*/ lQ$+JX;n(y
package com.javaeye.common.business; Tyd
h9I
6]ZO'Nwo
import java.io.Serializable; |6*Va%LYO-
import java.util.List; {=iyK/Uf
uJ8x
import org.hibernate.Criteria; K_&_z
import org.hibernate.HibernateException; vpV$$=Qwp
import org.hibernate.Session; Qsji0ikG
import org.hibernate.criterion.DetachedCriteria; 37jQ'O
U
import org.hibernate.criterion.Projections; 61>f(?s
import N iISJWk6'
`;/XK,m-
org.springframework.orm.hibernate3.HibernateCallback; uY]T:UVk
import R"{l[9j4>
`I#`:hj
org.springframework.orm.hibernate3.support.HibernateDaoS lRH0)5`
Bq{]Eh0%
upport; ,5.
<oDH
|*fNH(8&H
import com.javaeye.common.util.PaginationSupport; ,Z5Fea
cd&B?\I
public abstract class AbstractManager extends Fs)
qRl/Sl#F
HibernateDaoSupport { 4m\([EO
q)k{W>O
privateboolean cacheQueries = false; OfJd/D
jzMg'z/@J
privateString queryCacheRegion; `)2[ST
oLw|uU-|
publicvoid setCacheQueries(boolean gmDR{loX
h1c{?xH2r
cacheQueries){ K"^cq~
this.cacheQueries = cacheQueries; ;j!UY.i
} ^vW$XRnt
5{>>,pP&
publicvoid setQueryCacheRegion(String fp tIc#4
@(){/cF
queryCacheRegion){ KC]tY9 FK
this.queryCacheRegion = H0+:XF\M
q0g1EJar
queryCacheRegion; eo ?Oir)
} B/G3T
u uG
Z/c_kf[
publicvoid save(finalObject entity){ -%i#j>
getHibernateTemplate().save(entity); "/!'9na{QL
} vnZ4(
|(&oI(l5K
publicvoid persist(finalObject entity){ Vmtzig3w[
getHibernateTemplate().save(entity); 506V0]`/
} F1J#Y$q~L
IX.sy
publicvoid update(finalObject entity){ V]m^7^m3
getHibernateTemplate().update(entity); -f 4>MG
} 82s5VQ6
pl?kS8#U?
publicvoid delete(finalObject entity){ k,lqT>C
getHibernateTemplate().delete(entity); l#ZyB|
} %p*`h43;
rmQ\RP W
publicObject load(finalClass entity, F+3!uWUK
}k| g%HJ
finalSerializable id){ sjb-Me?
return getHibernateTemplate().load VfRs[3Q
3A d*,>!
(entity, id); P#v^"}.Wd
} "f<#.}8
=1IEpxh%
publicObject get(finalClass entity, ?yf_Dt
=E1tgrW
finalSerializable id){ {KsVK4\r
return getHibernateTemplate().get T\fudmj&
Az9J\V~"
(entity, id); 8F)=n \
} NA\ x<
+[_gyLN<5b
publicList findAll(finalClass entity){ Q K j1yG0i
return getHibernateTemplate().find("from $bFgsy*N2
#<UuI9
" + entity.getName()); AoIc9ElEX
} u]0!|Jd0
zu<>"5}]
publicList findByNamedQuery(finalString :v#8O~
@ct#s:t
namedQuery){ ,8~dz
return getHibernateTemplate Zikm?(J
]| z")gOE
().findByNamedQuery(namedQuery); 61kO1,Uz*
} y}Cj#I+a
4rm87/u*0
publicList findByNamedQuery(finalString query, )%BT*)x
X~%IM1+L;
finalObject parameter){ w0aHEvH/
return getHibernateTemplate 7>
)l{7
jOtzx"/)rE
().findByNamedQuery(query, parameter); dX0x
Kk%#
} =/m}rcDN
gkLr]zv
publicList findByNamedQuery(finalString query, oW8;^u
f@L\E>t
finalObject[] parameters){ =@%MV(
return getHibernateTemplate =^by0E2
cmae&Atotw
().findByNamedQuery(query, parameters); *%nX#mwz
} @YsL*zw
4 #G3ew
publicList find(finalString query){ [XxA.S)x3
return getHibernateTemplate().find *50ZinfoG
9a-]T=5Ee
(query); NXi,5
} IN>TsTo
N]*!8
publicList find(finalString query, finalObject
Re{ej
^,>}%1\
parameter){ (KZUvsS k
return getHibernateTemplate().find )2/b$i,JKk
%$^$'6\77
(query, parameter); 95VqaR,
} r^e-.,+
D8W(CE^}
public PaginationSupport findPageByCriteria '&+Z ,
ga,A'Z
(final DetachedCriteria detachedCriteria){ $*g{[&L|6
return findPageByCriteria ^g\h]RD}
-)<JBs>
(detachedCriteria, PaginationSupport.PAGESIZE, 0); WGluZhRuT3
} N:5b1TdI,
WI%zr2T
public PaginationSupport findPageByCriteria eUYG96Jw
4U:DJ_GN
(final DetachedCriteria detachedCriteria, finalint k#BU7Exij
(]oFB$
startIndex){ Af$0 o=".
return findPageByCriteria ?! !;XW
Ogn,1nm%
(detachedCriteria, PaginationSupport.PAGESIZE, oK%K+h
#xDDh`
startIndex); +38Lojb}
} Sv~PXi^`H
'w: tq
public PaginationSupport findPageByCriteria hl=oiUf[s
DM+sjn
(final DetachedCriteria detachedCriteria, finalint aIY$5^x
[sjrb?Xd
pageSize, oVAOGHE
finalint startIndex){ A7mMgb_
return(PaginationSupport) !Mm+bWn=mB
l^)o'YS y
getHibernateTemplate().execute(new HibernateCallback(){ HdDo
publicObject doInHibernate !N@Yh"c
Z8N@e<!*~8
(Session session)throws HibernateException { lrM.RM96
Criteria criteria = \z<ws&z3`$
}Z<D^Z~w
detachedCriteria.getExecutableCriteria(session); r@\,VD6J
int totalCount = g4?Q.'dZr
mOABZ#+Fk
((Integer) criteria.setProjection(Projections.rowCount "87O4
#$
a>#d=.
()).uniqueResult()).intValue(); (v9!g#
criteria.setProjection 0q-0zXlSL
ZK W@pW]U
(null); }//8$Z<(
List items = 94S .9A
$@XPL~4
criteria.setFirstResult(startIndex).setMaxResults 3^uL`ETm@
bf&.rJ0
(pageSize).list(); RI7qsm6RN
PaginationSupport ps = :5q^\xmmq
rerUM*0
new PaginationSupport(items, totalCount, pageSize, 30wYc &H
o;Hd W
startIndex); h'z+8X_t
return ps; OLhWkN,qA
} T<w*dX7F0K
}, true); cN0~;!{i
} XY&]T'A
g^Ugl=f,
public List findAllByCriteria(final /S-/SF:>g
[J[ysW})W
DetachedCriteria detachedCriteria){ 9u-M! $
return(List) getHibernateTemplate i!/h3%=
I_R5\l}O+D
().execute(new HibernateCallback(){ TZvBcNi
publicObject doInHibernate &z{dr~
~)oWSo5ll
(Session session)throws HibernateException { b6rzHnl{
Criteria criteria = 4DG 9`5.
A,-[/Z K/
detachedCriteria.getExecutableCriteria(session); %FXI lH5
return criteria.list(); 2`q^Q
} 7N-CtQnv
}, true); *)}Ap4[
} =N[V{2}q
8 RzF].)
public int getCountByCriteria(final k}+MvGq
HZ[68T[8b
DetachedCriteria detachedCriteria){ %Hh &u
.
Integer count = (Integer) <
|]i
Rz])wBv e
getHibernateTemplate().execute(new HibernateCallback(){ S|z(
publicObject doInHibernate =X%R*~!#Of
!/=9VD{U!
(Session session)throws HibernateException { =l?"=HF
Criteria criteria = qW` XA
.$}Z:,aB
detachedCriteria.getExecutableCriteria(session); <Bob#Tf
~
return .3g\[p
GSUOMy[M-
criteria.setProjection(Projections.rowCount @ B}c4,
[|m>vY!
()).uniqueResult(); &})4?5
} .yHHogbt
}, true); ID{Pzmt-
return count.intValue(); l72ie
} hCOy\[2$
} 5Fl
H8=vQy
/(WX!EEsB
}AeE|RNc
Npg5Z%+y
0N}
wD-
用户在web层构造查询条件detachedCriteria,和可选的 ;{F;e)${M
}y-AoG
startIndex,调用业务bean的相应findByCriteria方法,返回一个 8m13M5r
l yLK$B?/
PaginationSupport的实例ps。 s K$Sar
D3ZT''
ps.getItems()得到已分页好的结果集 iX9[Q0g=oQ
ps.getIndexes()得到分页索引的数组 "cz]bCr8
ps.getTotalCount()得到总结果数 ^0BF2&Zx
ps.getStartIndex()当前分页索引 jT wM<?
ps.getNextIndex()下一页索引 L;(3u'
ps.getPreviousIndex()上一页索引 <|>:UGAR
~n]2)>6
KWZNu&)
>x _:=%Wr+
+lf@O&w
wTgx(LtH
Vms7
Jay
连续看了两篇robbin有关DetachedCriteria的介绍,感觉真的不错 Ow&'sR'CX
Y;I(6`,Y
,尤其是上面的示例代码,让我着实觉得该对我原来的分页查询做 a_#eGe>
w!GU~0~3[
一下代码重构了。 [b)K@Ha
5jCEy*%P@
我把原本我的做法也提供出来供大家讨论吧: RE*S7[ge
Ms$7E
首先,为了实现分页查询,我封装了一个Page类: R~seUW7uv"
java代码: 1PT_1[eAR
s'fcAh,c6
,a?\i
JNb
/*Created on 2005-4-14*/ q_m#BE;t
package org.flyware.util.page; WTy8 N
e[VJ0 A=
/** nH3b<k;S
* @author Joa 0 S`b;f
* oT5rX
,8
*/ JXa%TpI:
E
publicclass Page { N6 }i>";_;
kI1{>vYD
/** imply if the page has previous page */ ?RjKP3P
privateboolean hasPrePage; %~v76;H<
bMK'J
/** imply if the page has next page */ MdTd$ 4J3
privateboolean hasNextPage; m#}{"d&J
GT`<jzAi Q
/** the number of every page */ 0T{Y_IG
privateint everyPage; 9[]"%6
gQzJ2LU(
/** the total page number */ %w|3:
privateint totalPage; ]V]@Zna@g
~6kA<(x
/** the number of current page */ pQm!Bt L
privateint currentPage; ]C:If h~
0R!}}*Ee>q
/** the begin index of the records by the current gu%'M:Xe
V/[,1W[B
query */ $!3t$-TSD
privateint beginIndex; gSo(PW)
I`}vdX)
EA{*%9 A
/** The default constructor */ h,jAtL!
public Page(){ q-)_Qco
"OAZ<
} Y
Z2VP
j!8+|eAkk
/** construct the page by everyPage {,mRMDEy
* @param everyPage v}*u[GWl]
* */ N)I
T?
public Page(int everyPage){ PHL@1K{)
this.everyPage = everyPage; CzsY=DBH=
} Dp |FyP_w
EQ`t:jc{
/** The whole constructor */ aiX;D/t?
public Page(boolean hasPrePage, boolean hasNextPage, r`"#c7)
/WgW e
T|iF/p]F
int everyPage, int totalPage, -v+^x`HR
int currentPage, int beginIndex){ BNm va
this.hasPrePage = hasPrePage; C-
Rie[
this.hasNextPage = hasNextPage; YaZ"&i
this.everyPage = everyPage; &-)Y[#\J
this.totalPage = totalPage; r0uXMr=Z96
this.currentPage = currentPage; wdDHRW0Y
this.beginIndex = beginIndex; JY8"TQ$x
} A-`J!xj#/
=Bqa<Js
/** ~acK$.#
* @return B91PlM.
* Returns the beginIndex. G+^$JN=
*/ |Ie`L("
publicint getBeginIndex(){ hBSJEP
return beginIndex; scEQDV
} r{jD,x2
!l~aRj-WZ
/** /{)cI^9
* @param beginIndex o-Fle, qf
* The beginIndex to set. xi^e =:;`
*/ /+U)!$zm*
publicvoid setBeginIndex(int beginIndex){ SpiC0
this.beginIndex = beginIndex; *K^O oS
} f0bV]<_9
]@}BdMlHp
/** )P+GklI{4
* @return 3NZFW{u
* Returns the currentPage. wupD
*/ 2 3w{h d
publicint getCurrentPage(){ cW^)$>A
return currentPage; i1Sc/
} O7*i;$!R
3s$.l}
/** To?
bp4
* @param currentPage a-2
{x2O
* The currentPage to set. zW`koRH@
*/ U+M?<4J)"
publicvoid setCurrentPage(int currentPage){ cyeDZ)
this.currentPage = currentPage; 0\^2HjsJ
} ]Wm ?<7H
&nw~gSe
/** Ou,_l
* @return ZTC1t_
* Returns the everyPage. z6r/
w
*/ ,PxQ[CGg
publicint getEveryPage(){ w o9f99
return everyPage; qyfxT Q5
} {S(T1ua
$s!meg@s
/** 7V``f:#d
* @param everyPage FQ1oqqr
* The everyPage to set. *lF%8k"Al
*/ 3(p6ak2lv
publicvoid setEveryPage(int everyPage){ Q8:ocEhR
this.everyPage = everyPage; o_m.MMEU
} g$LwXfg
&J M;jSz
/** }Cg~::,"
* @return N0hU~| /
* Returns the hasNextPage. IomJo
*/ #vwXx r
publicboolean getHasNextPage(){ kovzB]
return hasNextPage; ;>Qd )'
} ha~s<
I
N,$o'\l
/** shZ<j7gqI
* @param hasNextPage 8QBL:7<
* The hasNextPage to set. dF]8>jBOL
*/ Ls*=mh~IY
publicvoid setHasNextPage(boolean hasNextPage){ 2=+ ,jX{
this.hasNextPage = hasNextPage; EIm\!'R]
} R?SHXJ%'
cLP@0`^H
/** %n,bPa>T
* @return 1R9/AP
* Returns the hasPrePage. 1 to<at-NN
*/ ibw;BU
publicboolean getHasPrePage(){ EBLoRW=8ld
return hasPrePage; ;mlIWn
} ]~ UkD*Ct
_S1uJ~j;E
/** VNXVuM )c
* @param hasPrePage nP31jm+A
* The hasPrePage to set. j-|0&X1C
*/ zSCPp6
publicvoid setHasPrePage(boolean hasPrePage){ "PtH
F`mo
this.hasPrePage = hasPrePage; *^_!W'T{j
} \M@8# k|
h_!"CF<n
/** gv-k}2u_
* @return Returns the totalPage. 'Y Zs6rcJ
* [G/X
*/ 3Gv
i!h7
publicint getTotalPage(){ &X(-C9'j
return totalPage; zt0 zKXw
} DboqFh#]=h
$@wkQ%
/** fh<G&E8
p
* @param totalPage bnQO}G
* The totalPage to set. .5xg;Qg\Y
*/ *JXJ
2
publicvoid setTotalPage(int totalPage){ P s;:g0
this.totalPage = totalPage; TKX# /
} ^+<uHd>
.`].\Zykf
} _R6> Ayw*
)`-]nMc
$)V4Eu;
-2_$zk*n
zPYa@0I
上面的这个Page类对象只是一个完整的Page描述,接下来我写了一 ?2;G_P+
)I4t l/
个PageUtil,负责对Page对象进行构造: r kl7p?
java代码: UtrbkuT
pnU
g:R@
hg @Jpg
/*Created on 2005-4-14*/ 9n7d
"XD2
package org.flyware.util.page; 0<9TyN6
B"v=Fr[
import org.apache.commons.logging.Log; H_FhHX.2(
import org.apache.commons.logging.LogFactory; 8 Hn{CJ~'
k_B^2=
/** H"l'E9k.&p
* @author Joa a{W-+t
* qT4s*kqr
*/ 4{KsCd)
publicclass PageUtil { p%-9T>og
?da 3Azp
privatestaticfinal Log logger = LogFactory.getLog IpxjP\
kZNZ?A<D
(PageUtil.class); aJ5R0Y,
%ZK}y{u\
/** =qRVKz
* Use the origin page to create a new page P'8E8_M}
* @param page Apn#o2
* @param totalRecords k|5nu-B0v
* @return :*1w;>o)n
*/ -,&Xp>u\
publicstatic Page createPage(Page page, int A-FwNo2"%
0"N %Vm
totalRecords){ w6_}]
&F
return createPage(page.getEveryPage(), L;[*F-+jD
d,)L, J
page.getCurrentPage(), totalRecords); F`u~Jx8.*
} y(k2p
Kf.b
<wP{
/** 6X7_QBC)
* the basic page utils not including exception (Wn'.|^%
H =jnCGk
handler ]!N5jbA@
* @param everyPage =j0V/=
* @param currentPage [>;O'>
* @param totalRecords A?/?9Gr
* @return page \<} nn?~n
*/ L;"<8\vWB
publicstatic Page createPage(int everyPage, int jo^*R'}
?6dtvz;K+?
currentPage, int totalRecords){ $gNCS:VG*
everyPage = getEveryPage(everyPage); _$0Ix6y,
currentPage = getCurrentPage(currentPage); t>xV]W<
int beginIndex = getBeginIndex(everyPage, iYf4 /1IG,
FyEl@ }W
currentPage); C6n4OU
int totalPage = getTotalPage(everyPage, SxDE3A-:
;Yj}9[p;T
totalRecords); TI332,eL
boolean hasNextPage = hasNextPage(currentPage, _MU'he^W
P*SXfb"HC
totalPage); aI{[W;43T
boolean hasPrePage = hasPrePage(currentPage); J:5n/m^A
~&x%;cnv_
returnnew Page(hasPrePage, hasNextPage, P(`IY+
everyPage, totalPage, JI&>w-~D
currentPage, ezn>3?S
Ut+m m\7
beginIndex); bA)Xjq)Rr
} ^?2txLv,6
-n9e-0
privatestaticint getEveryPage(int everyPage){ Hpt)(Nz:
return everyPage == 0 ? 10 : everyPage; AS7!FD6b
} eZcm3=WV|
*s^5BLI9
privatestaticint getCurrentPage(int currentPage){ ZZTV
>:
return currentPage == 0 ? 1 : currentPage; Lh}he:k+
} wb}tN7~Y;
9YJb~tuZ73
privatestaticint getBeginIndex(int everyPage, int b%kh:NV{S
J: LSGj;R
currentPage){ t`AD9
H"\!
return(currentPage - 1) * everyPage; N ]duv~JS
} 1jL?z6S
1pV"<,t
privatestaticint getTotalPage(int everyPage, int R/#*~tPi8
MWl@smRh
totalRecords){ tT 7$2 9
int totalPage = 0; iB?@(10}ES
Ur`v*LT}~
if(totalRecords % everyPage == 0) =9c24j
totalPage = totalRecords / everyPage; (:\hor%
else 6-3l6q
totalPage = totalRecords / everyPage + 1 ; mT.F$Y9
B$bsh.
return totalPage; h2q]!01XP
} 5?b9[o+D
%;<