Spring+Mybatis實現動態SQL查詢
在報表類應用中,通常需要根據不同的維度去組合復雜的查詢條件,然后構造SQL去執行查詢。如果只是通過在程序中簡單地拼接SQL語句,工作量會非常大,而且代碼可能也非常難以維護。Mybatis支持動態SQL查詢功能,可以通過配置動態的SQL來簡化程序代碼中復雜性,不過,這個頗有點XML編程的韻味,通過XML來處理復雜的數據判斷、循環的功能,其實也很好理解。
准備工作
下面,我們首先創建一個MySQL示例表,如下所示:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
CREATE
TABLE
`traffic_info` (
`id`
int
(11)
NOT
NULL
AUTO_INCREMENT,
`domain`
varchar
(64)
NOT
NULL
,
`traffic_host`
varchar
(64)
NOT
NULL
,
`
month
`
varchar
(8)
NOT
NULL
,
`monthly_traffic`
int
(11)
DEFAULT
'0'
,
`global_traffic_rank`
int
(11)
DEFAULT
'0'
,
`native_traffic_rank`
int
(11)
DEFAULT
'0'
,
`rank_in_country`
varchar
(64)
DEFAULT
NULL
,
`address`
varchar
(200)
DEFAULT
NULL
,
`email`
varchar
(50)
DEFAULT
NULL
,
`traffic_type`
int
(2)
DEFAULT
'-1'
,
`status`
int
(2)
DEFAULT
'0'
,
`created_at`
date
DEFAULT
NULL
,
`updated_at`
timestamp
NOT
NULL
DEFAULT
CURRENT_TIMESTAMP
ON
UPDATE
CURRENT_TIMESTAMP
,
`f1`
varchar
(255)
DEFAULT
NULL
,
`f2`
varchar
(255)
DEFAULT
NULL
,
`f3`
varchar
(255)
DEFAULT
NULL
,
PRIMARY
KEY
(`id`),
UNIQUE
KEY
`idx_traffic` (`domain`,`
month
`,`traffic_type`)
) ENGINE=MyISAM AUTO_INCREMENT=1
DEFAULT
CHARSET=utf8;
|
這個表用來存儲域名的流量信息,流量信息我們從互聯網上像Alexa、Compete、Quantcast等提供商獲取,通過Crawler抓取的方式實現。我們先從簡單的查詢做起,只是根據某個字段進行查詢,說明如何配置使用Mybatis,這里面也包含如何與Spring進行集成。
配置實踐
下面是用到的一些資源的定義:
- org.shirdrn.mybatis.TrafficInfo類
該類對應於traffic_info表中一條記錄的數據,我們簡單取幾個字段,如下所示:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package
org.shirdrn.mybatis;
import
java.io.Serializable;
public
class
TrafficInfo
implements
Serializable {
private
static
final
long
serialVersionUID = -8696613205078899594L;
int
id;
String domain;
String month;
int
monthlyTraffic;
public
int
getId() {
return
id;
}
public
void
setId(
int
id) {
this
.id = id;
}
public
String getDomain() {
return
domain;
}
public
void
setDomain(String domain) {
this
.domain = domain;
}
public
String getMonth() {
return
month;
}
public
void
setMonth(String month) {
this
.month = month;
}
public
int
getMonthlyTraffic() {
return
monthlyTraffic;
}
public
void
setMonthlyTraffic(
int
monthlyTraffic) {
this
.monthlyTraffic = monthlyTraffic;
}
@Override
public
String toString() {
return
"[id="
+ id +
", domain="
+ domain +
", month="
+
month +
", monthlyTraffic="
+ monthlyTraffic +
"]"
;
}
}
|
- org.shirdrn.mybatis.mapper.TrafficInfoMapper接口類
該類定義了一個與SQL配置進行映射的基本操作,實際的SQL配置有專門的XML文件來進行配置。該接口定義了如下操作:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
package
org.shirdrn.mybatis.mapper;
import
java.util.List;
import
java.util.Map;
import
org.shirdrn.mybatis.TrafficInfo;
public
interface
TrafficInfoMapper {
/**
* 根據指定id去查詢記錄,結果至多只有一條
* @param id
* @return
*/
TrafficInfo getTrafficInfo(
int
id);
/**
* 根據指定的domain參數查詢記錄,返回一個記錄的列表
* @param domain
* @return
*/
List<TrafficInfo> getTrafficInfoList(String domain);
/**
* 根據一個 字段domain進行查詢,但是存在多個domain的值,傳入一個數組
* @param domains
* @return
*/
List<TrafficInfo> getMultiConditionsList(String[] domains);
/**
* 根據多個字段進行查詢,每個字段可能有多個值,所以參數是Map類型
* @param conditions
* @return
*/
List<TrafficInfo> getMapConditionsList(Map<String, Object> conditions);
}
|
上面接口中定義的操作,一個比一個復雜,我們通過這一系列操作來說明在Mybatis中如果使用各種查詢功能。
- org/shirdrn/mybatis/mapper/TrafficInfoMapper.xml映射配置文件
這個文件TrafficInfoMapper.xml對應了上面的org.shirdrn.mybatis.mapper.TrafficInfoMapper中定義的操作,通過XML的方式將對應的SQL查詢構造出來,這個是Mybatis的核心功能。該文件的內容示例如下所示:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<!
DOCTYPE
mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<
mapper
namespace
=
"org.shirdrn.mybatis.mapper.TrafficInfoMapper"
>
<
resultMap
type
=
"TrafficInfo"
id
=
"tfMap"
>
<
id
property
=
"id"
column
=
"id"
/>
<
result
property
=
"domain"
column
=
"domain"
/>
<
result
property
=
"month"
column
=
"month"
/>
<
result
property
=
"monthlyTraffic"
column
=
"monthlyTraffic"
/>
</
resultMap
>
<
select
id
=
"getTrafficInfo"
resultType
=
"TrafficInfo"
parameterType
=
"int"
>
SELECT * FROM domain_db.traffic_info WHERE id = #{id}
</
select
>
<
select
id
=
"getTrafficInfoList"
resultType
=
"TrafficInfo"
parameterType
=
"string"
>
SELECT * FROM domain_db.traffic_info WHERE domain = #{domain}
</
select
>
<
select
id
=
"getMultiConditionsList"
resultMap
=
"tfMap"
>
SELECT * FROM domain_db.traffic_info WHERE domain IN
<
foreach
collection
=
"array"
index
=
"index"
item
=
"domain"
open
=
" ("
separator
=
","
close
=
")"
>
#{domain}
</
foreach
>
</
select
>
<
select
id
=
"getMapConditionsList"
resultMap
=
"tfMap"
>
SELECT * FROM domain_db.traffic_info WHERE domain IN
<
foreach
collection
=
"domains"
index
=
"index"
item
=
"domain"
open
=
" ("
separator
=
","
close
=
")"
>
#{domain}
</
foreach
>
AND status = 0 AND month IN
<
foreach
collection
=
"months"
index
=
"index"
item
=
"month"
open
=
" ("
separator
=
","
close
=
")"
>
#{month}
</
foreach
>
</
select
>
</
mapper
>
|
如果你之前用過ibatis,應該很熟悉上面這個配置文件。上面:
namespace指定該SQL映射配置文件的Mapper接口類,其中定義了基本的SQL查詢操作(以我們給出的例子為例);
resultMap中的type的值這里是一個別名,當然也可以使用對應的具體類全名(包名+類名),我們會在Mybatis的總的映射配置文件中進行配置,詳見后面說明;
select是查詢SQL的配置,可以通過不同的元素進行動態構造,如if、foreach等;
- Mybatis全局映射配置文件sqlMapConfig.xml
該文件可以指定數據庫連接池配置、別名配置、SQL映射配置文件組等內容,這里示例的配置內容如下所示:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<!
DOCTYPE
configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
<
configuration
>
<
typeAliases
>
<
typeAlias
type
=
"org.shirdrn.mybatis.TrafficInfo"
alias
=
"TrafficInfo"
/>
</
typeAliases
>
<
mappers
>
<
mapper
resource
=
"org/shirdrn/mybatis/mapper/TrafficInfoMapper.xml"
/>
</
mappers
>
</
configuration
>
|
- Spring配置文件applicationContext.xml
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns:context
=
"http://www.springframework.org/schema/context"
xmlns:aop
=
"http://www.springframework.org/schema/aop"
xmlns:tx
=
"http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
<
bean
class
=
"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
>
<
property
name
=
"systemPropertiesModeName"
value
=
"SYSTEM_PROPERTIES_MODE_OVERRIDE"
/>
<
property
name
=
"ignoreResourceNotFound"
value
=
"true"
/>
<
property
name
=
"locations"
>
<
list
>
<
value
>classpath*:/proxool.properties</
value
>
</
list
>
</
property
>
</
bean
>
<
context:component-scan
base-package
=
"org.shirdrn.mybatis"
/>
<
aop:aspectj-autoproxy
proxy-target-class
=
"true"
/>
<
aop:config
proxy-target-class
=
"true"
/>
<
bean
id
=
"dataSource"
class
=
"org.shirdrn.mybatis.utils.ProxoolDataSource"
>
<
property
name
=
"driver"
value
=
"${jdbc-0.proxool.driver-class}"
/>
<
property
name
=
"driverUrl"
value
=
"${jdbc-0.proxool.driver-url}"
/>
<
property
name
=
"user"
value
=
"${jdbc-0.user}"
/>
<
property
name
=
"password"
value
=
"${jdbc-0.password}"
/>
<
property
name
=
"alias"
value
=
"${jdbc-0.proxool.alias}"
/>
<
property
name
=
"prototypeCount"
value
=
"${jdbc-0.proxool.prototype-count}"
/>
<
property
name
=
"maximumActiveTime"
value
=
"${jdbc-0.proxool.maximum-active-time}"
/>
<
property
name
=
"maximumConnectionCount"
value
=
"${jdbc-0.proxool.maximum-connection-count}"
/>
<
property
name
=
"minimumConnectionCount"
value
=
"${jdbc-0.proxool.minimum-connection-count}"
/>
<
property
name
=
"simultaneousBuildThrottle"
value
=
"${jdbc-0.proxool.simultaneous-build-throttle}"
/>
<
property
name
=
"verbose"
value
=
"${jdbc-0.proxool.verbose}"
/>
<
property
name
=
"trace"
value
=
"${jdbc-0.proxool.trace}"
/>
<
property
name
=
"houseKeepingTestSql"
value
=
"${jdbc-0.proxool.house-keeping-test-sql}"
/>
<
property
name
=
"houseKeepingSleepTime"
value
=
"${jdbc-0.proxool.house-keeping-sleep-time}"
/>
<
property
name
=
"maximumConnectionLifetime"
value
=
"${jdbc-0.proxool.maximum-connection-lifetime}"
/>
</
bean
>
<
bean
id
=
"dataSource0"
class
=
"org.jdbcdslog.ConnectionPoolDataSourceProxy"
>
<
property
name
=
"targetDSDirect"
ref
=
"dataSource"
/>
</
bean
>
<!-- http://mybatis.github.io/spring/zh/ -->
<
bean
id
=
"sqlSessionFactory"
class
=
"org.mybatis.spring.SqlSessionFactoryBean"
>
<
property
name
=
"dataSource"
ref
=
"dataSource0"
/>
<
property
name
=
"configLocation"
value
=
"classpath:sqlMapConfig.xml"
/>
</
bean
>
<
bean
id
=
"trafficInfoMapper"
class
=
"org.mybatis.spring.mapper.MapperFactoryBean"
>
<
property
name
=
"mapperInterface"
value
=
"org.shirdrn.mybatis.mapper.TrafficInfoMapper"
/>
<
property
name
=
"sqlSessionFactory"
ref
=
"sqlSessionFactory"
/>
</
bean
>
<
bean
id
=
"trafficInfoService"
class
=
"org.shirdrn.mybatis.TrafficInfoService"
>
<
property
name
=
"trafficInfoMapper"
ref
=
"trafficInfoMapper"
/>
</
bean
>
</
beans
>
|
簡單說明一下:
dataSource使用的Proxool連接池組件;
sqlSessionFactory是Mybatis的SessionFactory,注入了前面獲取到的dataSource,同時指定了Mybatis的總的映射配置文件classpath:sqlMapConfig.xml,屬性名為configLocation;
trafficInfoMapper直接由Spring的org.mybatis.spring.mapper.MapperFactoryBean進行代理,需要注入屬性mapperInterface(即我們定義的SQL Mapper操作的接口類)和sqlSessionFactory(前面的SessionFactory實例);
trafficInfoService是我們最終在其中進行調用的服務類,注入了我們定義的SQL Mapper接口類的實例trafficInfoMapper。
- org.shirdrn.mybatis.TrafficInfoService服務類
為簡單起見,我們就不定義服務接口了,直接在該類中實現,調用SQL Mapper中預定義的SQL查詢操作,實現代碼如下所示:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package
org.shirdrn.mybatis;
import
java.util.List;
import
java.util.Map;
import
org.shirdrn.mybatis.mapper.TrafficInfoMapper;
public
class
TrafficInfoService {
private
TrafficInfoMapper trafficInfoMapper;
public
void
setTrafficInfoMapper(TrafficInfoMapper trafficInfoMapper) {
this
.trafficInfoMapper = trafficInfoMapper;
}
public
TrafficInfo getTrafficInfo(
int
id) {
return
trafficInfoMapper.getTrafficInfo(id);
}
public
List<TrafficInfo> getTrafficInfoList(String domain) {
return
trafficInfoMapper.getTrafficInfoList(domain);
}
public
List<TrafficInfo> getMultiConditionsList(String[] domains) {
return
trafficInfoMapper.getMultiConditionsList(domains);
}
List<TrafficInfo> getMapConditionsList(Map<String, Object> conditions) {
return
trafficInfoMapper.getMapConditionsList(conditions);
}
}
|
按照上面的配置,我們就能夠實現從單個字段的查詢,到多個字段的組合復雜查詢。可以通過與實際編寫代碼來控制這些邏輯相比較,使用Mybatis可能配置上相對復雜一些,但是或得到的好處是非常多的,如代碼可維護性好,看起來配置比較直觀,出錯的幾率會大大減小。實際上,如果熟練的這種配置方式,就會在實際開發過程中,更好地去處理更加復雜的統計查詢條件的組合邏輯。
測試用例
測試用例可以檢測我們上面的配置是否生效,實現代碼:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
package
org.shirdrn.mybatis;
import
java.util.Arrays;
import
java.util.HashMap;
import
java.util.List;
import
java.util.Map;
import
org.junit.Test;
import
org.junit.runner.RunWith;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.test.context.ContextConfiguration;
import
org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith
(SpringJUnit4ClassRunner.
class
)
@ContextConfiguration
(locations = {
"classpath:/applicationContext*.xml"
})
public
class
TestTrafficInfoService {
@Autowired
private
TrafficInfoService trafficInfoService;
@Test
public
void
getTraffic() {
int
id =
1196
;
TrafficInfo result = trafficInfoService.getTrafficInfo(id);
System.out.println(result);
}
@Test
public
void
getTrafficList() {
String domain =
"make-the-cut.com"
;
List<TrafficInfo> results = trafficInfoService.getTrafficInfoList(domain);
System.out.println(results);
}
@Test
public
void
getMultiConditionsList() {
String[] domains =
new
String[] {
"make.tv"
,
" make-the-cut.com"
,
"makgrills.com"
,
"makino.com"
};
List<TrafficInfo> results = trafficInfoService.getMultiConditionsList(domains);
System.out.println(results);
}
@Test
public
void
getMapConditionsList() {
String[] domains =
new
String[] {
"make.tv"
,
" make-the-cut.com"
,
"makgrills.com"
,
"makino.com"
};
List<String> months = Arrays.asList(
new
String[] {
"201203"
,
"201204"
,
"201205"
});
Map<String, Object> conditions =
new
HashMap<String, Object>(
2
);
conditions.put(
"domains"
, domains);
conditions.put(
"months"
, months);
List<TrafficInfo> results = trafficInfoService.getMapConditionsList(conditions);
System.out.println(results);
}
}
|
查詢進階
這里,給出一個實際的例子,是對每日報表的一個統計實例,為簡單起見,只拿出2張表做LEFT JOIN連接。這個需求,要求查詢時可以對每個維度取過得查詢條件值,如對於維度osName,值可以使包含Android、IOS,對於另一個維度statDate,可以取最近2天(昨天和前天),等等,並且,這些組合條件可有可無。
對應的Mybatis映射配置文件,內容如下所示:
|
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<!
DOCTYPE
mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<
mapper
namespace
=
"org.shirdrn.data.mappers.DailyAppUserMapper"
>
<
resultMap
id
=
"dailyAppUserMap"
type
=
"DailyAppUser"
>
<
id
property
=
"id"
column
=
"id"
/>
<
result
property
=
"primaryCategoryId"
column
=
"primary_category_id"
/>
<
result
property
=
"primaryCategoryName"
column
=
"primary_category_name"
/>
<
result
property
=
"secondaryCategoryId"
column
=
"secondary_category_id"
/>
<
result
property
=
"secondaryCategoryName"
column
=
"secondary_category_name"
/>
<
result
property
=
"cooperationMode"
column
=
"cooperation_mode"
/>
<
result
property
=
"merchantId"
column
=
"merchant_id"
/>
<
result
property
=
"merchantName"
column
=
"merchant_name"
/>
<
result
property
=
"osName"
column
=
"osName"
/>
<
result
property
=
"channelId"
column
=
"channel_id"
/>
<
result
property
=
"channelName"
column
=
"channel_name"
/>
<
result
property
=
"version"
column
=
"version"
/>
<
result
property
=
"statDate"
column
=
"stat_date"
/>
<
result
property
=
"newUserOpen"
column
=
"new_user_open"
/>
<
result
property
=
"activeUserOpen"
column
=
"active_user_open"
/>
<
result
property
=
"activeUserPlay"
column
=
"active_user_play"
/>
<
result
property
=
"oldUserOpen"
column
=
"old_user_open"
/>
<
result
property
=
"oldUserPlay"
column
=
"old_user_play"
/>
<
result
property
=
"averageTime"
column
=
"average_time"
/>
<
result
property
=
"newUserAverageTime"
column
=
"new_user_average_time"
/>
<
result
property
=
"oldUserAverageTime"
column
=
"old_user_average_time"
/>
<
result
property
=
"newUserOpen2Retention"
column
=
"new_user_open_2retention"
/>
<
result
property
=
"newUserOpen3Retention"
column
=
"new_user_open_3retention"
/>
<
result
property
=
"newUserOpen7Retention"
column
=
"new_user_open_7retention"
/>
<
result
property
=
"newUserOpen15Retention"
column
=
"new_user_open_15retention"
/>
<
result
property
=
"newUserOpen30Retention"
column
=
"new_user_open_30retention"
/>
</
resultMap
>
<
select
id
=
"getDailyAppUserListByPage"
resultMap
=
"dailyAppUserMap"
>
<
include
refid
=
"getDailyAppUserList"
/>
LIMIT #{offset}, #{limit}
</
select
>
<
select
id
=
"getDailyAppUserListForReport"
resultMap
=
"dailyAppUserMap"
>
<
include
refid
=
"getDailyAppUserList"
/>
</
select
>
<
sql
id
=
"getDailyAppUserList"
>
SELECT
d.id AS id,
d.primary_category_id AS primary_category_id,
d.primary_category_name AS primary_category_name,
d.secondary_category_id AS secondary_category_id,
d.secondary_category_name AS secondary_category_name,
d.cooperation_mode AS cooperation_mode,
d.merchant_id AS merchant_id,
d.osName AS osName,
d.channel_id AS channel_id,
(CASE WHEN d.channel_name IS NOT NULL THEN d.channel_name ELSE d.channel_id END) AS channel_name,
d.version AS version,
d.stat_date AS stat_date,
d.new_user_open AS new_user_open,
d.new_user_play AS new_user_play,
d.active_user_open AS active_user_open,
d.active_user_play AS active_user_play,
d.old_user_open AS old_user_open,
d.old_user_play AS old_user_play,
d.average_time AS average_time,
d.new_user_average_time AS new_user_average_time,
d.old_user_average_time AS old_user_average_time,
d.new_user_open_2retention AS new_user_open_2retention,
d.new_user_open_3retention AS new_user_open_3retention,
d.new_user_open_7retention AS new_user_open_7retention,
d.new_user_open_15retention AS new_user_open_15retention,
d.new_user_open_30retention AS new_user_open_30retention,
d.uninstall_cnt AS uninstall_cnt,
m.merchant_name AS merchant_name
FROM daily_app_user d
LEFT JOIN merchant m ON d.merchant_id=m.id
WHERE d.stat_date = #{statDate}
<
if
test
=
"osNames!=null"
>
AND d.osName IN
<
foreach
collection
=
"osNames"
index
=
"index"
item
=
"osName"
open
=
" ("
separator
=
","
close
=
")"
>
#{osName}
</
foreach
>
</
if
>
<
if
test
=
"channelNames!=null"
>
AND
<
foreach
collection
=
"channelNames"
index
=
"index"
item
=
"channelName"
open
=
" ("
separator
=
" OR "
close
=
")"
>
(d.channel_name LIKE CONCAT('%', CONCAT(#{channelName}, '%')))
</
foreach
>
</
if
>
<
if
test
=
"versions!=null"
>
AND d.version IN
<
foreach
collection
=
"versions"
index
=
"index"
item
=
"version"
open
=
" ("
separator
=
","
close
=
")"
>
#{version}
</
foreach
>
</
if
>
<
if
test
=
"merchantNames!=null"
>
AND
<
foreach
collection
=
"merchantNames"
index
=
"index"
item
=
"merchantName"
open
=
" ("
separator
=
" OR "
close
=
")"
>
(m.merchant_name LIKE CONCAT('%', CONCAT(#{%merchantName%}, '%')))
</
foreach
>
</
if
>
<
if
test
=
"primaryCategories!=null"
>
AND d.primary_category_id IN
<
foreach
collection
=
"primaryCategories"
index
=
"index"
item
=
"primaryCategory"
open
=
" ("
separator
=
","
close
=
")"
>
#{primaryCategory}
</
foreach
>
</
if
>
<
if
test
=
"secondaryCategories!=null"
>
AND d.secondary_category_id IN
<
foreach
collection
=
"secondaryCategories"
index
=
"index"
item
=
"secondaryCategory"
open
=
" ("
separator
=
","
close
=
")"
>
#{secondaryCategory}
</
foreach
>
</
if
>
<
if
test
=
"cooperationModes!=null"
>
AND d.cooperation_model IN
<
foreach
collection
=
"cooperationModes"
index
=
"index"
item
=
"cooperationMode"
open
=
" ("
separator
=
","
close
=
")"
>
#{cooperationMode}
</
foreach
>
</
if
>
</
sql
>
</
mapper
>
|
上述映射配置對應的Mapper定義,接口如下所示:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
package
org.shirdrn.data.mappers;
import
java.util.List;
import
java.util.Map;
import
org.shirdrn.data.beans.DailyAppUser;
public
class
DailyAppUserMapper {
List<DailyAppUser> getDailyAppUserListByPage(Map<String, Object> conditions);
List<DailyAppUser> getDailyAppUserListForReport(Map<String, Object> conditions);
}
|
需要說明的是,如果多個表,一定要設置好Mapper映射配置中每個select元素的resultMap屬性,屬性值就是前部分的resultMap定義的id。如果只從單個表查詢數據,完全可以使用resultType,對應resultMap元素中配置的type屬性所指定的別名。
實際上,我們需要通過Map來傳遞參數,也就是把查詢的條件值都收集起來,然后放到Map中,示例如下:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
Map<String, Object> conditions =
new
HashMap<String, Object>();
if
(osNames !=
null
) {
conditions.put(DailyAppUserMapper.KEY_OS_NAMES, osNames);
}
if
(channelNames !=
null
) {
conditions.put(DailyAppUserMapper.KEY_CHANNEL_NAMES, channelNames);
}
if
(versions !=
null
) {
conditions.put(DailyAppUserMapper.KEY_VERSIONS, versions);
}
if
(merchantNames !=
null
) {
conditions.put(DailyAppUserMapper.KEY_MERCHANT_NAMES, merchantNames);
}
if
(primaryCategories !=
null
) {
conditions.put(DailyAppUserMapper.KEY_PRIMARY_CATEGORIES, primaryCategories);
}
if
(secondaryCategories !=
null
) {
conditions.put(DailyAppUserMapper.KEY_SECONDARY_CATEGORIES, secondaryCategories);
}
if
(cooperationModes !=
null
) {
conditions.put(ChannelDayMapper.KEY_COOPERATION_MODES, cooperationModes);
}
|
上面對應的DailyAppUserMapper中定義的一些Key常量名稱,要和Mapper配置文件中foreach元素的collection屬性值一致。
