Hibernate3 提供了DetachedCriteria,使得我们可以在Web层构造 XOzd{
nTweQ
detachedCriteria,然后调用业务层Bean,进行动态条件查询,根 *;wPAQE
2#:h.8
据这一功能,我设计了通用的抽象Bean基类和分页类支持,代码来 x-km)2x=W
\4`~J@5Y
自于Quake Wang的javaeye-core包的相应类,然后又做了很多修改 eEkFZx
l Ud/^u`
。 hGx)X64Mw
i*' 6"
分页支持类: oq4*m[
YT6<1-E#
java代码: |"vUC/R2&
gf^"sfNk
W~J@v@..4
package com.javaeye.common.util; y_:i'Ri.
'Jww}^h1
import java.util.List; 0 V:z(r
, [xDNl[Y|
publicclass PaginationSupport { ]?p&sI4
/AX1LYlr
publicfinalstaticint PAGESIZE = 30; xJ)hGPrAl
gN|[n.W4
privateint pageSize = PAGESIZE; Q=[AP+
.olDmFQD
privateList items; lV/-jkR
j'#M'W3@
privateint totalCount; Z> jk\[
gf()NfUvRH
privateint[] indexes = newint[0]; 8_6\>hW&
T> cvV
privateint startIndex = 0; 3>Ts7
wM
dHV3d'.P
public PaginationSupport(List items, int Mvq5s +.
`x2Q:&.H`
totalCount){ %y{#fZHc
setPageSize(PAGESIZE); 5T)qn`%
setTotalCount(totalCount); z;GR(;w/
setItems(items); ,b2YUb]U
setStartIndex(0); Z?yMy zT
} :uE:mY%R
?z>7&
public PaginationSupport(List items, int u-g2*(ZT
jdsN ZV
totalCount, int startIndex){ >6ul\xMU
setPageSize(PAGESIZE); jEwfa_Q%
setTotalCount(totalCount); \^O#)&5 V
setItems(items); 6 KuB<od
setStartIndex(startIndex); ^a&-GhX;
} B -XM(Cj
/g}2QmvH
public PaginationSupport(List items, int
<WN?
x_&=IyU0j
totalCount, int pageSize, int startIndex){ 7'g'qUW+~
setPageSize(pageSize); [jb3lO$Xa
setTotalCount(totalCount); {[~cQgCI
setItems(items); tW4|\-E"s4
setStartIndex(startIndex); cNX,%
} J
ik+t\A
!;'#fxW[
publicList getItems(){ fx&b*OC
return items; Id`?yt
} !QK~l
XRi/O)98o
publicvoid setItems(List items){ ts
BPQ 8Ne
this.items = items; o4I&?d7;"
} rv>K0= t0
3KFw0(S/
publicint getPageSize(){ rO8Q||@>A
return pageSize; WVaIC $Y
} Z|&Y1k-h
)aY^k|I
publicvoid setPageSize(int pageSize){ H"hL+F ^
this.pageSize = pageSize; xh>/bU!>
} WG
9f>kE
;_0)f
publicint getTotalCount(){ JWG7QH
return totalCount; qQR>z
} |7Z,z0 ?V
F2yM2Ldx
publicvoid setTotalCount(int totalCount){ 5Ah-aDBj
if(totalCount > 0){ =$5[uI2
this.totalCount = totalCount; iUh_rX9A"
int count = totalCount / /qF7^9LtaY
okX\z[X
pageSize; XKp %7;
if(totalCount % pageSize > 0) B$7m@|p!
count++; my")/e
indexes = newint[count]; a<Pi J?
for(int i = 0; i < count; i++){ *Kt7"J
indexes = pageSize * h~elF1dG
w;Qo9=-
i; ?{q w
/&
} fRT:@lV
}else{ #I@]8U#,":
this.totalCount = 0; ~0?p @8
} p)tac*US
} k`js~/Xv
`.%;|"xR
publicint[] getIndexes(){ NpD}7t<EF
return indexes; nx=#QLi
} uYL6g:]+ZC
!5`}s9hsF_
publicvoid setIndexes(int[] indexes){ |d D! @K
this.indexes = indexes; s~/]nz]"J
} Yx&cnDx
D4'?
V
Iz
publicint getStartIndex(){ ~N[hY1}X[
return startIndex; g)$Pvfc
} E yNI]XEj
h9+ylHW_cp
publicvoid setStartIndex(int startIndex){ g8XGZW!
if(totalCount <= 0) B<1*p,z
this.startIndex = 0; dGIu0\J\$
elseif(startIndex >= totalCount) zwU8i VDe
this.startIndex = indexes j)-D.bY0
. F#mT h
[indexes.length - 1]; F:pXdU-xf
elseif(startIndex < 0) 8<n8joO0
this.startIndex = 0; q'(WIv@
else{ ";s5It
this.startIndex = indexes .?I!/;=[
X""'}X|O
[startIndex / pageSize]; YfMe69/0I
} +"3eh1q[
} )lw7W9
7Wb.(` a<
publicint getNextIndex(){ 2zFdKs,
int nextIndex = getStartIndex() + iLmU|jdE
ys#M*
{?
pageSize; ]3={o3[:
if(nextIndex >= totalCount) CPVKz
return getStartIndex(); \=N
tbBL$[
else do[K-r
return nextIndex; yFpHRfF}
} N7oMtlvL[w
s=%HT fw
publicint getPreviousIndex(){ i[\[xfk
int previousIndex = getStartIndex() - IN%>46e`
#Ejly2C,
pageSize; K&)a3Z=(.
if(previousIndex < 0) *<Ddn&_
return0; v,D_^?] @
else rM?D7a{q
return previousIndex; k oHY
AF
} mKsj7
8W$="s2
} BPG)m,/b
$AXz/fGV
zr[~wM
E`?BaCrG~
抽象业务类 ]~K&mNo
java代码: %ly;2HIk
:%>8\q>UX
i*^K)SI8
/** ##s!-.T
* Created on 2005-7-12 z9'0&G L
*/ +%<Jr<~W
package com.javaeye.common.business; aJ}sYf^
'D/AL\1{p(
import java.io.Serializable; z9+94<J
import java.util.List; w-#0k.T
SRD&Uf0M
import org.hibernate.Criteria; kyjH~mK4
import org.hibernate.HibernateException; CYn}wkz
import org.hibernate.Session; -$0S#/)Z
import org.hibernate.criterion.DetachedCriteria; \6'A^cE/PX
import org.hibernate.criterion.Projections; )F$Stg3e
import L_.}z)S[\
=
]@xXVf/
org.springframework.orm.hibernate3.HibernateCallback; GawO>7w8
import <=-\so(
K$]QzPXS
org.springframework.orm.hibernate3.support.HibernateDaoS IW1\vfe
\'EWur"
upport; \k,bz0
i3"sArP"|
import com.javaeye.common.util.PaginationSupport; 2G-?
P"4l@
\l2 s^7G_
public abstract class AbstractManager extends e)xWQ=,C
UZ!hk*PF
HibernateDaoSupport { bGJUu#
D1n2Z:9
privateboolean cacheQueries = false; iN*>Z(b"
Vj]kJ,j\y
privateString queryCacheRegion; uUe\[-~
I I8nz[s
publicvoid setCacheQueries(boolean +o3g]0
!LJ.L?9qw
cacheQueries){ N 93E;B
this.cacheQueries = cacheQueries; ^%d{i'9?
} @ 7?_Yw
k:TfE6JZ
publicvoid setQueryCacheRegion(String 'O`3FI
)|{{}w~`
queryCacheRegion){ @5y(>>C}8%
this.queryCacheRegion = &~%@QC/
e%U*~{m+
queryCacheRegion; (<l2 ^H
} 752wK|o0|;
c-.>C)
publicvoid save(finalObject entity){ is(!_Iv
getHibernateTemplate().save(entity); 9NP l]iA)
} //Gvk|O1
pPZ/ O6
publicvoid persist(finalObject entity){ ~Z]vr6?$h
getHibernateTemplate().save(entity); /;rN/ot2o
} '-IT@}
UTQKlwPa
publicvoid update(finalObject entity){ V,+[XB
getHibernateTemplate().update(entity); 'j_H{kQy
} sZ"U=6R
XijLS7Aw|
publicvoid delete(finalObject entity){ U!/nD~A
getHibernateTemplate().delete(entity); |y%].y)
} +q/h:q.TV
Z0eBx
publicObject load(finalClass entity, _vdxxhJ=P3
xacLlX+
finalSerializable id){ ^\&g^T%
return getHibernateTemplate().load |{)xC=
#(
sNk,^Ax
(entity, id); 8 %j{4$
} qb4;l\SfT
Tr0V6TS7
publicObject get(finalClass entity, 66<\i ltUQ
yim$y,=d
finalSerializable id){
9JV
3
return getHibernateTemplate().get ocqB-C]
g0j4<\F2\
(entity, id); ZQ9oZHU m
} 'ZH<g8:=@
+;*(a3Gp
publicList findAll(finalClass entity){ k!/_/^{
return getHibernateTemplate().find("from p/1}>F|i
Zng` oFD
" + entity.getName()); C^" Hj
} G0QXf
6L2*gO:r?
publicList findByNamedQuery(finalString *:T>~ilF
4@]xn
namedQuery){ gZ"{{#:}
return getHibernateTemplate @=0r3
{QS@Ugf
().findByNamedQuery(namedQuery); reM
} v^],loi<V
2]NAs9aZ
publicList findByNamedQuery(finalString query, :gD0EqV
!+A"Lej
finalObject parameter){ 9U<)_E<y
return getHibernateTemplate TFC!u0Y"$
'^ e/F)0
().findByNamedQuery(query, parameter); Ef`5fgp?
S
} 9{%g-u\
^[seK)S=
publicList findByNamedQuery(finalString query, F%.9fUo
7 xp1\j0
finalObject[] parameters){ < k+fKl
return getHibernateTemplate T]nR=uK6LL
_.OMjUBZT
().findByNamedQuery(query, parameters); *)NR$9lGv
} <Sprp]n
7
g2aT`=&Z
publicList find(finalString query){ V"7<[u]K|
return getHibernateTemplate().find GIC"-l1\
}+dM1 O
(query);
_
7BF+*T
} /+4^.Q*
Ql q#Zdru
publicList find(finalString query, finalObject I[g?Ju >
T+K` ^xv_L
parameter){ t*Vao
return getHibernateTemplate().find Uin k
K#l
-?
(query, parameter); ~^rey
} )]/i
o5=)~D{/G3
public PaginationSupport findPageByCriteria 7cUR.PI#Q
_Sly7_
(final DetachedCriteria detachedCriteria){ >}<29Ii
return findPageByCriteria ,#E3,bu6_4
L876$
(detachedCriteria, PaginationSupport.PAGESIZE, 0); +p9LE4g7Q
} p|h.@do4
tDC0-N&6S~
public PaginationSupport findPageByCriteria ~j/bCMEf!
#MhNdH#
(final DetachedCriteria detachedCriteria, finalint L,C? gd@"
fqcU5l[v,
startIndex){ ;g:
U[cE
return findPageByCriteria Em~7D]Y
Qb!!J4|!
(detachedCriteria, PaginationSupport.PAGESIZE, 5T$}Oy1
Bmx(qE
startIndex); RNv{n
mf
} l-<3{!
d98))G~W
public PaginationSupport findPageByCriteria vF9*tK'
b4Ricm
(final DetachedCriteria detachedCriteria, finalint ruZYehu1W
Q3'fz 9v
pageSize, /^++As0pY
finalint startIndex){ h]kn%?fpmB
return(PaginationSupport) /Jh1rck
lemVP'cn
getHibernateTemplate().execute(new HibernateCallback(){ <e;jWK
publicObject doInHibernate <_"B}c/2$
>F/XZC
(Session session)throws HibernateException { rlR
!&
Criteria criteria = gzVtxDh
w?kGi>7E
detachedCriteria.getExecutableCriteria(session); LMAmpVo
int totalCount = TG1P=g5h
inYM+o!Ub
((Integer) criteria.setProjection(Projections.rowCount ID)^vwn
[-4KY4R
()).uniqueResult()).intValue(); DH:J
criteria.setProjection ^uia`sOP4
A'GlCp
(null); (@+h5@J[`I
List items = vwVVBG;t
%Nj #0YF]
criteria.setFirstResult(startIndex).setMaxResults NeHx2m+
#;`Oj
(pageSize).list(); a>U6Ag<
PaginationSupport ps = I6+2>CUGo
^pj>9%
new PaginationSupport(items, totalCount, pageSize, GMz8B-vk
_l<mu? "
startIndex); &M[MEO`t8
return ps; cQX:%Ix=
} %MN.O-Lc
}, true); s2^B(wP
} flP>@i:e6
EJ`Q8uz
public List findAllByCriteria(final COSTV>s;
r@k"4ce-
DetachedCriteria detachedCriteria){ ?mMW*ico
return(List) getHibernateTemplate f3G1r5x
$eBQH
().execute(new HibernateCallback(){ GF'f[F6oI
publicObject doInHibernate T#@{G,N
RDJ+QOVKg
(Session session)throws HibernateException { &tj0M.-
Criteria criteria = zb :kanb-
r+S;B[Vd
detachedCriteria.getExecutableCriteria(session); qfYG.~`5
return criteria.list(); 2JZdw
} J4>;[\%m
}, true);
WK==j1
} >3PMnI
OxQYNi2
public int getCountByCriteria(final `\N]wlB2/b
8eJE>g1J
DetachedCriteria detachedCriteria){ ?Vc0)
Integer count = (Integer) JoJukoy}F
CZ(`|;BC*
getHibernateTemplate().execute(new HibernateCallback(){ sn"fK=,#g
publicObject doInHibernate [b/o$zR
,h&a9:+i
(Session session)throws HibernateException { -Sz_mr
Criteria criteria = ar$*a>'?
W`M6J}oG
detachedCriteria.getExecutableCriteria(session); :q
(&$
return aSL6zye
,
%;|0
criteria.setProjection(Projections.rowCount ]Blf9h7
zvO:"w}
()).uniqueResult(); 2*2:-ocl$
} 1~\M!SQ)
}, true); ,j178EX
return count.intValue(); IQnIaZ
} pRi<cO
} cWa)#:JOV
BStk&b
H}ZQ?uK;
g"K>5Cb
q0./O|Dj
3t"4TjAy
用户在web层构造查询条件detachedCriteria,和可选的 #;Tz[0
] ]U<UJ
startIndex,调用业务bean的相应findByCriteria方法,返回一个 CBYX]
9LnN$e
PaginationSupport的实例ps。 p/Sbt/R
38ES($
ps.getItems()得到已分页好的结果集 F'}'(t+oAm
ps.getIndexes()得到分页索引的数组 ~T7B$$
ps.getTotalCount()得到总结果数 pW0dB_
ps.getStartIndex()当前分页索引 N;oQ^B'
ps.getNextIndex()下一页索引 /hrT
ps.getPreviousIndex()上一页索引 +ECDD'^!
^jB8Q
%.v{N6
hY5WJ;
wFh8?Z3u_
\`V$
'B{.
wm{3&m
连续看了两篇robbin有关DetachedCriteria的介绍,感觉真的不错 sZWaV4
UV
4>N
,尤其是上面的示例代码,让我着实觉得该对我原来的分页查询做 tY${M^^<J
?^mi3VM
一下代码重构了。 r[(xjn
hVT~~n`Rj
我把原本我的做法也提供出来供大家讨论吧: fA%z*\
AUVgPXOwd
首先,为了实现分页查询,我封装了一个Page类: keW~ NM
java代码: '3]p29v{
uJHu>M}~
a {}|Bf<
/*Created on 2005-4-14*/ )Au6Nf
package org.flyware.util.page; |{*}|
.?p\n7
/** ECdvX0*a
* @author Joa P&d"V<
* > oA?6x
*/ |6UtW{2I/
publicclass Page { g*)K/Z0pJ$
- K%,^6
/** imply if the page has previous page */ +uWDP.
privateboolean hasPrePage; IKtB;
JXG"M#{
/** imply if the page has next page */ |py6pek|
privateboolean hasNextPage; SvN9aD1
B/5=]R
/** the number of every page */ j*e6vX
privateint everyPage; ms($9 Lv/
e__@GBG
/** the total page number */ 4L-:*b_v\
privateint totalPage; +$xeoxU>;
Uie?9&3
/** the number of current page */ n .!Ym
X4
privateint currentPage; /vY_Y3k#
a9n^WOJ6
/** the begin index of the records by the current A*./,KT
AC
O)Dt(Y
query */ N=:5eAza
privateint beginIndex; ,D\GGRw
O8|5KpXd@
]
2DH;
/** The default constructor */ ,f}UGd[a
public Page(){ +(xeT+J
{k4CEt;
} w;Q;[:y
TI9UXa:V\
/** construct the page by everyPage ^gVT$A
* @param everyPage \MxoZ
* */ kc7lc|'z
public Page(int everyPage){ 5L:1A2Z?c
this.everyPage = everyPage;
tvXW
} %{3
aW>yx
<
RCLI|
/** The whole constructor */ Ca%g_B0t
public Page(boolean hasPrePage, boolean hasNextPage, 5XuT={o
ZEYgK)^
yq+!czlZ
int everyPage, int totalPage, Z%GTnG|rG
int currentPage, int beginIndex){ tO_H!kP
this.hasPrePage = hasPrePage; jBaB@LO9G
this.hasNextPage = hasNextPage;
^ kST
this.everyPage = everyPage; &z"krM]G
this.totalPage = totalPage; `mye}L2I
this.currentPage = currentPage; ~&\ f|%
this.beginIndex = beginIndex; I@Xn3oN
} DK0.R]&4(
yr#5k`&\_
/** ExHKw~y9
* @return /ywD{*
* Returns the beginIndex. 'qdPw%d
*/ =)O,`.M.Y
publicint getBeginIndex(){ yL;M"L
return beginIndex; X0-IRJ[
} 2oJb)CB
~2EH OO{
/** 9w9jpe#
* @param beginIndex ~[k%oA%W
* The beginIndex to set. B3Jgd,[
*/ 8A{_GH{:
publicvoid setBeginIndex(int beginIndex){ .qk]$LJF7
this.beginIndex = beginIndex; l{w#H|]
} PpLU
$,xnU.n
/** +.y
.Mp
* @return G8W#<1LE
* Returns the currentPage. P;PQeXKw
*/ b|SE<\
publicint getCurrentPage(){ ]d[ge6
return currentPage; H A}f,),G
} GRy-+#,b"
7FN<iI&7\
/** 9+,R`v
* @param currentPage c:83LZ
* The currentPage to set. 1_$xSrwcF
*/ "uLjIIl
publicvoid setCurrentPage(int currentPage){ 5>6PH+Oq
this.currentPage = currentPage; C@N1ljXJT
} DoC(Z)o
CukC6ub
/** PNW \*;j
* @return |%~+2m
* Returns the everyPage. )m3q2W
*/ cNzt%MjP
publicint getEveryPage(){ wOB azWa
return everyPage; PiFD^w
} !~_zm*CqbZ
= sAn,ri
/** K; FW
* @param everyPage 0=w K:Ex
* The everyPage to set. x*oWa,
*/ =O|c-k,f@
publicvoid setEveryPage(int everyPage){ 9*6]&:fm
this.everyPage = everyPage; ]E3U
J!!
} KCe13!
U=bEA1*@0
/** F#xa`*AP
* @return '2hbJk
* Returns the hasNextPage. N__H*yP
*/ 4^6.~6a
publicboolean getHasNextPage(){ !#wd Ve_(
return hasNextPage; #G9 adK5
} ]IJRnVp%
- ~O'vLG
/** P`U<7xF~
* @param hasNextPage ujW1+Oj=~
* The hasNextPage to set. iq?#rb P#I
*/ 8GJdRL(
publicvoid setHasNextPage(boolean hasNextPage){ _9:@Vl]Q@
this.hasNextPage = hasNextPage; l2b{u
GE
} /Tz85 [%6
h%d^Gq~
/** Gt9wR
* @return X7UBopm&
* Returns the hasPrePage. j-]&'-h}#
*/ ;??ohA"{5
publicboolean getHasPrePage(){ vUQFQ
return hasPrePage; Z.&\=qiY
} =fc:6JR
zH)_vW
/** ? y},,
* @param hasPrePage pw>m.=9|y
* The hasPrePage to set. dOa!htx]
*/ 5,+\`!g
publicvoid setHasPrePage(boolean hasPrePage){ x_@i(oQ:_
this.hasPrePage = hasPrePage; &!7+Yb(1
} _3Q8n|
l52a\/
/** p5c8YfM
* @return Returns the totalPage. #;9H@:N
* /<)-q-W;
*/ DrS~lTf=>
publicint getTotalPage(){ {sN"(H4$
return totalPage; "#^MUQ!a
} _rQUE^9
6=|&tE
/** ^kj=<+ v#
* @param totalPage :,]S}R
* The totalPage to set. +Al>2 ~
*/ ESQ!@G/n
publicvoid setTotalPage(int totalPage){ eVy2|n9rH
this.totalPage = totalPage; tR`S#rk
} !oSLl.fQd
J2Et-Cz 1
} \4~AI=aw,T
o 5Zyh26
\tt'm\_
Jgx8-\8
c;!g
上面的这个Page类对象只是一个完整的Page描述,接下来我写了一 p,#6
@*
.Oo/y0E^
个PageUtil,负责对Page对象进行构造: VK
.^v<Yo
java代码: U ^#?&u
hYt7kq!"
N)OCSeh
/*Created on 2005-4-14*/ ")\aJ8
package org.flyware.util.page; ]S/G\z
l:e9y $_)
import org.apache.commons.logging.Log; }5S2p@W)
import org.apache.commons.logging.LogFactory; T&}Ye\%
?z.Isvn
/** !'c| N9
* @author Joa W7e4pR?w
* w!,QxrOV~
*/ OXtBJYe
publicclass PageUtil { o<\6Rm
3"i% {
privatestaticfinal Log logger = LogFactory.getLog : ^(nj7D
cIZc:
(PageUtil.class); &c^7O#j
oS!/|#mn
/** x70N8TQ_gK
* Use the origin page to create a new page kkfCAM
* @param page NhlJ3/J j
* @param totalRecords N{pa)
/
* @return nZW4} ~0j
*/ @,6ST0xT (
publicstatic Page createPage(Page page, int `QLowna
m^0A?jBrR
totalRecords){ GMb!Q0I8
return createPage(page.getEveryPage(), 7 6*hc
|>JmS
page.getCurrentPage(), totalRecords); KZ\dB;W<|
} 1Zq
{d}26 $<$]
/** <j"O%y.
* the basic page utils not including exception J[+Tj@n'
XPVV+.
handler ]
zIfC>@R
* @param everyPage U,ELqi \
* @param currentPage E 8^sy*f
* @param totalRecords y[p6y[r*
* @return page ]-rczl|o
*/ 5:_~mlfi
publicstatic Page createPage(int everyPage, int ,*w>z
Ctj8tK$D
currentPage, int totalRecords){ m`a>,%}P"
everyPage = getEveryPage(everyPage); v['AB4
currentPage = getCurrentPage(currentPage); _X?y,#
int beginIndex = getBeginIndex(everyPage, !xJFr6G~8
m^Rd Iy)
currentPage); h+
TB]
int totalPage = getTotalPage(everyPage, ~q5-9{ma
%N1T{
totalRecords); 'iGMn_&
boolean hasNextPage = hasNextPage(currentPage, R>` ih&,)
6|i`@|#
totalPage); y#nSk%"t"
boolean hasPrePage = hasPrePage(currentPage); f5N<3 m=
zH'!fhcy
returnnew Page(hasPrePage, hasNextPage, WY3_7k8u
everyPage, totalPage, T+3k$G[e/
currentPage, =^ gvZ|]
(c
1u{
beginIndex); ScsWnZ
} @s3aR*ny$
f};!m=b
privatestaticint getEveryPage(int everyPage){ !HbqbS22
return everyPage == 0 ? 10 : everyPage; `7F@6n
} i<0D
Z_rub
NH<Y1t
privatestaticint getCurrentPage(int currentPage){ '=-s1c@^
return currentPage == 0 ? 1 : currentPage; `s#sE.=o
} ;}n|,g>
!7ph,/P$7
privatestaticint getBeginIndex(int everyPage, int ;,()wH
nmTm(?yE
currentPage){ t@l(xns V
return(currentPage - 1) * everyPage; qZ `n Zi
} |C&%S"*+D
C+ZQB)gn
privatestaticint getTotalPage(int everyPage, int 1y_fQ+\2A
XPGL3[w\V
totalRecords){ z(c@(UD-_
int totalPage = 0; "``W6W-(
bEoB;]
if(totalRecords % everyPage == 0) 'Wo?%n
totalPage = totalRecords / everyPage; nNd`]F^U
else -[i40
1
totalPage = totalRecords / everyPage + 1 ; 6~:W(E}
}wa}hIqx
return totalPage; )< &B