前言:
mybatis在持久層框架中還是比較火的,一般項目都是基于ssm。雖然mybatis可以直接在xml中通過SQL語句操作數(shù)據(jù)庫忌锯,很是靈活凭疮。但正其操作都要通過SQL語句進行,就必須寫大量的xml文件膛腐,很是麻煩睛约。mybatis-plus就很好的解決了這個問題。
簡介
MyBatis-Plus(簡稱 MP)是一個 MyBatis的增強工具哲身,在 MyBatis 的基礎(chǔ)上只做增強不做改變辩涝,為簡化開發(fā)、提高效率而生勘天。
官網(wǎng):https://mp.baomidou.com/guide
整合
1怔揩、創(chuàng)建表
CREATE TABLE user (
id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主鍵',
user_name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
age INT(11) DEFAULT NULL COMMENT '年齡',
email VARCHAR(50) DEFAULT NULL COMMENT '郵箱',
create_time DATETIME DEFAULT NULL COMMENT '創(chuàng)建時間',
update_time DATETIME DEFAULT NULL COMMENT '修改時間'
) ENGINE=INNODB CHARSET=UTF8;
初始化數(shù)據(jù)
INSERT INTO user (id, user_name, age, email,create_time,update_time) VALUES
(1, 'Jone', 18, 'test1@baomidou.com',now(),now()),
(2, 'Jack', 20, 'test2@baomidou.com',now(),now()),
(3, 'Tom', 28, 'test3@baomidou.com',now(),now()),
(4, 'Sandy', 21, 'test4@baomidou.com',now(),now()),
(5, 'Billie', 24, 'test5@baomidou.com',now(),now());
now()函數(shù)獲取當前時間
2、創(chuàng)建springboot項目并導(dǎo)入依賴
修改mysql版本為5.1.47
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
MyBatis-Plus依賴:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
官網(wǎng)最新版本已經(jīng)為: 3.3.2
脯丝,使用Boot版本為2.2.4
商膊,不使用最新的,哈哈。
3巾钉、配置
1翘狱、在 application.yml 配置文件中添加數(shù)據(jù)庫的相關(guān)配置:
server:
port: 1998
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8
username: root
password: 980402
driver-class-name: com.mysql.jdbc.Driver
mysql5跟mysql8在連接的時候會有點不同:
(1)、mysql 5 驅(qū)動不同 com.mysql.jdbc.Driver
(2)砰苍、mysql 8 驅(qū)動不同com.mysql.cj.jdbc.Driver潦匈、需要增加時區(qū)的配置 serverTimezone=GMT+8
4、編碼
1赚导、編寫實體類 User.java
//生成getter,setter等函數(shù)
@Data
//生成全參數(shù)構(gòu)造函數(shù)
@AllArgsConstructor
//生成無參構(gòu)造函數(shù)
@NoArgsConstructor
public class User {
private Long id;
private String userName;
private Integer age;
private String email;
private Date createTime;
private Date updateTime;
}
2茬缩、編寫Mapper接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gongj.mybatisplus.entity.User;
//繼承基本的類 BaseMapper
public interface UserMapper extends BaseMapper<User> {
}
3、在 Spring Boot 啟動類中添加 @MapperScan 注解吼旧,掃描 Mapper 所在目錄
@SpringBootApplication
@MapperScan("com.gongj.mybatisplus.mapper")
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
5凰锡、測試
5.1、測試查詢?nèi)?/h4>
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
UserMapper userMapper;
@Test
void testSelect(){
// 參數(shù)是一個 Wrapper ,條件構(gòu)造器掂为,這里我們先不用 null 查詢?nèi)坑脩? List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
5.2裕膀、在yml配置sql日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
UserMapper userMapper;
@Test
void testSelect(){
// 參數(shù)是一個 Wrapper ,條件構(gòu)造器掂为,這里我們先不用 null 查詢?nèi)坑脩? List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
再次查詢
5.3測試添加
@Test
void testInsert() {
User user = new User();
user.setUserName("gongj");
user.setEmail("19908488818@163.com");
user.setAge(23);
user.setCreateTime(new Date());
user.setUpdateTime(new Date());
int insert = userMapper.insert(user);//幫我們自動生成id
System.out.println("id=" + user.getId()); //id會自動回填
}
MyBatisPlus主鍵策略:
public enum IdType {
AUTO(0), //數(shù)據(jù)庫id自增
NONE(1), // 未設(shè)置主鍵id
INPUT(2), // 用戶輸入id
//以下三種只有當插入對象ID 為空,才自動填充勇哗。
ID_WORKER(3), // 默認的全局唯一id
UUID(4), //全局唯一uuid
ID_WORKER_STR(5); //字符串全局唯一ID ID_WORKER的字符串表示方式
}
修改默認的主鍵策略
在實體類字段上 加上注解:@TableId(type = xxx)
@TableId(type = IdType.AUTO)
private Long id;
修改了主鍵策略為自增昼扛,數(shù)據(jù)庫字段一定要是自增!
5.4測試自動填充
在上面添加操作中我們是手動給創(chuàng)建時間欲诺、修改時間賦值抄谐,能不能自動化完成呢? MyBatisPlus提供了這樣的配置。
1扰法、自定義實現(xiàn)類 MyMetaObjectHandler
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
// 插入時的填充策略
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
// 更新時的填充策略
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
2蛹含、實體類字段屬性增加注解
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
填充類型:
public enum FieldFill {
/**
* 默認不處理
*/
DEFAULT,
/**
* 插入填充字段
*/
INSERT,
/**
* 更新填充字段
*/
UPDATE,
/**
* 插入和更新填充字段
*/
INSERT_UPDATE
}
再次測試添加操作:
@Test
void testInsert() {
User user = new User();
user.setUserName("gongj2");
user.setEmail("19908488818@163.com");
user.setAge(223);
int insert = userMapper.insert(user);//幫我們自動生成id
System.out.println("id=" + user.getId());
}
效果杠杠的。
5.5修改測試
@Test
void testUpdate() {
User user = new User();
user.setId(1276042184872341506L);
user.setUserName("ggggggggggg");
user.setAge(555);
int insert = userMapper.updateById(user);
System.out.println("id=" + user.getId());
}
所有的sql都是自動幫你動態(tài)配置的塞颁!也可以看到updateTime的自動填充也生效了浦箱。
6、樂觀鎖
當要更新一條記錄的時候殴边,希望這條記錄沒有被別人更新
樂觀鎖實現(xiàn)方式:
1憎茂、取出記錄時珍语,獲取當前version
2锤岸、更新時,帶上這個version
3板乙、執(zhí)行更新時是偷, set version = newVersion where version = oldVersion
4、如果version不對募逞,就更新失敗
6.1數(shù)據(jù)庫增加version字段蛋铆!
6.2實體類增加version屬性并加注解!
@Version
private Integer version;
6.3插件配置
@Configuration
public class MybatisPlusConfig {
/**
* 樂觀鎖插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
6.4放接、測試修改
@Test
void testVserion(){
// 1刺啦、查詢用戶信息
User user = userMapper.selectById(1276056567094583298L);
user.setAge(55);
user.setUserName("yuanj");
//user 就是前臺傳入的值
userMapper.updateById(user);
}
我們注意一下update的sql語句
UPDATE user SET create_time=?, update_time=?, user_name=?, version=?, email=?, age=? WHERE id=? AND version=?
version做為了修改條件。
特別說明:
- 支持的數(shù)據(jù)類型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整數(shù)類型下
newVersion = oldVersion + 1
-
newVersion
會回寫到entity
中 - 僅支持
updateById(id)
與update(entity, wrapper)
方法 - 在
update(entity, wrapper)
方法下,wrapper
不能復(fù)用!!!
7纠脾、查詢
7.1玛瘸、根據(jù)id查詢
@Test
void selectById(){
userMapper.selectById(1276056567094583298L);
}
7.2、根據(jù)id批量查詢
@Test
public void selectBatchIds(){
// 根據(jù)id批量查詢
List<User> users = userMapper.selectBatchIds(Arrays.asList(1276056567094583298L, 5, 3));
}
7.3苟蹈、根據(jù)Map 自定義條件查詢
@Test
public void selectByMap(){
// 根據(jù)Map 自定義條件查詢
Map map = new HashMap();
map.put("user_name","yuanj");
List<User> users = userMapper.selectByMap(map);
}
8糊渊、分頁查詢
1、配置攔截器組件
/**
* 分頁插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
2慧脱、編碼
@Test
public void selectPage(){
Page<User> userPage = new Page<>();
userPage.setCurrent(1L); //當前是第幾頁 默認為1
userPage.setSize(2); //每頁大小
IPage<User> userIPage = userMapper.selectPage(userPage, null);
System.out.println("當前頁" + userIPage.getCurrent()); //當前頁
System.out.println("總頁數(shù)" + userIPage.getPages()); //總頁數(shù)
System.out.println("返回數(shù)據(jù)" + userIPage.getRecords()); //返回數(shù)據(jù)
System.out.println("每頁大小" + userIPage.getSize()); //每頁大小
System.out.println("滿足符合條件的條數(shù)" + userIPage.getTotal()); //滿足符合條件的條數(shù)
System.out.println("下一頁" + userPage.hasNext()); //下一頁
System.out.println("上一頁" + userPage.hasPrevious()); //上一頁
}
9渺绒、刪除
刪除分為物理刪除和邏輯刪除。
1、物理刪除
1.1根據(jù)id物理刪除
@Test
void deleteById(){
//根據(jù)id物理刪除
int i = userMapper.deleteById(1L);
}
1.2根據(jù)id物理批量刪除
@Test
void deleteBatchIds(){
//批量刪除
int i = userMapper.deleteBatchIds(Arrays.asList(2L, 4L));
System.out.println(i); //受影響的行數(shù)為2
}
2宗兼、邏輯刪除
2.1躏鱼、在數(shù)據(jù)表中增加一個 deleted 字段
2.2、實體類中增加屬性并加上注解
@TableLogic
private Integer deleted;
2.3殷绍、測試
再次執(zhí)行刪除操作
@Test
void deleteById(){
//根據(jù)id刪除
int i = userMapper.deleteById(3L);
System.out.println(i);
}
可以看到sql是修改語句挠他。
2.4執(zhí)行查詢:
@Test
void testSelect(){
// 參數(shù)是一個 Wrapper ,條件構(gòu)造器篡帕,這里我們先不用 null 查詢?nèi)坑脩? List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
可以看到id為3的數(shù)據(jù)已經(jīng)查詢不出來了殖侵。
10、條件構(gòu)造器
1镰烧、查詢創(chuàng)建時間在2020-6-15到2020-7-5并且郵箱不等于空并不等于null的數(shù)據(jù)
@Test
void select1(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//查詢創(chuàng)建時間在2020-6-15到2020-7-5并且郵箱不等于空并不等于null的數(shù)據(jù)
queryWrapper.between("create_time","2020-06-01","2020-07-05").ne("email","").isNotNull("email");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach((System.out::println));
}
Preparing: SELECT id,deleted,create_time,update_time,user_name,version,email,age FROM
user WHERE deleted=0 AND (create_time BETWEEN ? AND ? AND
email <> ? AND email IS NOT NULL)
2拢军、模糊查詢
@Test
void select2(){
//模糊查詢
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.likeLeft("user_name","J"); //左模糊 %J
List<User> users = userMapper.selectList(queryWrapper);
users.forEach((System.out::println));
}
3、子查詢并根據(jù)id倒序
@Test
void select3(){
//子查詢并根據(jù)id倒序
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id","select id from user where id < 4");
queryWrapper.orderByDesc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach((System.out::println));
}
Preparing: SELECT id,deleted,create_time,update_time,user_name,version,email,age
FROM user WHERE deleted=0 AND (id IN (select id from user where id < 4))
ORDER BY id DESC
4怔鳖、分頁多條件查詢
//分頁多條件查詢
@Test
void select4(){
//查詢年齡大于等于28歲的信息并分頁
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.ge("age",28);
Page<User> userPage = new Page<>();
userPage.setCurrent(1L);
userPage.setSize(2);
IPage<User> userIPage = userMapper.selectPage(userPage, queryWrapper);
List<User> records = userIPage.getRecords();
records.forEach((System.out::println));
}
SELECT COUNT(1) FROM user WHERE deleted = 0 AND (age >= ?)
SELECT id,deleted,create_time,update_time,user_name,version,email,age,cid
FROM user WHERE deleted=0 AND (age >= ?) LIMIT ?,?
11茉唉、關(guān)聯(lián)查詢
1、新建一個card表
CREATE TABLE card (
cid BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主鍵',
cname VARCHAR(30) DEFAULT NULL COMMENT '卡片名'
) ENGINE=INNODB CHARSET=UTF8;
INSERT INTO card (cid,cname) VALUES
(1,'健身卡'),
(2,'KTV卡'),
(3,'校園卡')
2结执、在user表中創(chuàng)建cid列
并將user表和card表關(guān)聯(lián)起來
3度陆、編碼
3.1、card實體
//生成getter,setter等函數(shù)
@Data
//生成全參數(shù)構(gòu)造函數(shù)
@AllArgsConstructor
//生成無參構(gòu)造函數(shù)
@NoArgsConstructor
public class Card {
private Long cid;
private String cname;
}
3.2献幔、user實體
加入cid和cards屬性
private Long cid;
private List<Card> cards;
3.3懂傀、UserMapper新增方法
List<User> userJoinCard();
3.4、UserMapper.xml
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gongj.mybatisplus.mapper.UserMapper">
<resultMap id="userJoinCardMap" type="com.gongj.mybatisplus.entity.User">
<id column="id" property="id"/>
<result property="userName" column="user_name"></result>
<result property="age" column="age"></result>
<result property="createTime" column="create_time"></result>
<result property="updateTime" column="update_time"></result>
<result property="email" column="email"></result>
<result property="email" column="email"></result>
<result property="version" column="version"></result>
<result property="deleted" column="deleted"></result>
<result property="cid" column="cid"></result>
<collection property="cards" column="cid" ofType="com.gongj.mybatisplus.entity.Card">
<id column="cid" property="cid"></id>
<result column="cname" property="cname"></result>
</collection>
</resultMap>
<select id="userJoinCard" resultMap="userJoinCardMap">
select u.id,u.user_name,u.email,u.age,u.version,u.create_time,u.update_time,u.deleted,u.cid,c.cid,c.cname from user u left join card c on u.cid = c.cid
</select>
</mapper>
resultMap包含的元素:
<resultMap id="唯一的標識" type="映射的pojo對象">
<id column="表的主鍵字段" jdbcType="字段類型" property="映射pojo對象的主鍵屬性" />
<result column="表的一個字段" jdbcType="字段類型" property="映射到pojo對象的一個屬性"/>
<association property="pojo的一個對象屬性" javaType="pojo關(guān)聯(lián)的pojo對象">
<id column="關(guān)聯(lián)pojo對象對應(yīng)表的主鍵字段" jdbcType="字段類型" property="關(guān)聯(lián)pojo對象的主席屬性"/>
<result column="任意表的字段" jdbcType="字段類型" property="關(guān)聯(lián)pojo對象的屬性"/>
</association>
<!-- 集合中的property須為oftype定義的pojo對象的屬性-->
<collection property="pojo的集合屬性" ofType="集合中的pojo對象">
<id column="集合中pojo對象對應(yīng)的表的主鍵字段" jdbcType="字段類型" property="集合中pojo對象的主鍵屬性" />
<result column="可以為任意表的字段" jdbcType="字段類型" property="集合中的pojo對象的屬性" />
</collection>
</resultMap>
如果collection標簽是使用嵌套查詢蜡感,格式如下:
注意:<collection>標簽中的column:要傳遞給select查詢語句的參數(shù)蹬蚁,如果傳遞多個參數(shù),格式為column= ” {參數(shù)名1=表字段1,參數(shù)名2=表字段2} 郑兴;
<collection column="傳遞給嵌套查詢語句的字段參數(shù)" property="pojo對象中集合屬性" ofType="集合屬性中的pojo對象" select="嵌套的查詢語句" >
</collection>
https://www.cnblogs.com/kenhome/p/7764398.html
3.5犀斋、調(diào)用
@Test
void joinQuery(){
List<User> users = userMapper.userJoinCard();
users.forEach((System.out::println));
}
可以看到就是一條關(guān)聯(lián)查詢sql,然后在resultMap中進行字段映射情连。
3.6叽粹、collection的另外一種寫法,也可以叫分布查詢
CardMapper
public interface CardMapper extends BaseMapper<Card> {
public Card selectById();
}
CardMapper.xml
<?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="com.gongj.mybatisplus.mapper.CardMapper">
<resultMap id="CardMap" type="com.gongj.mybatisplus.entity.Card">
<id column="cid" property="cid"></id>
<result column="cname" property="cname"></result>
</resultMap>
<select id="selectById" resultMap="CardMap">
select * from card where cid = #{cid}
</select>
</mapper>
userMapper
List<User> userJoinCard2();
UserMapper.xml
<resultMap id="userJoinCardMap2" type="com.gongj.mybatisplus.entity.User">
<id column="id" property="id"/>
<result property="userName" column="user_name"></result>
<result property="age" column="age"></result>
<result property="createTime" column="create_time"></result>
<result property="updateTime" column="update_time"></result>
<result property="email" column="email"></result>
<result property="email" column="email"></result>
<result property="version" column="version"></result>
<result property="deleted" column="deleted"></result>
<result property="cid" column="cid"></result>
<collection property="cards" column="cid" ofType="com.gongj.mybatisplus.entity.Card" select="com.gongj.mybatisplus.mapper.CardMapper.selectById">
</collection>
</resultMap>
<select id="userJoinCard2" resultMap="userJoinCardMap2">
select u.id,u.user_name,u.email,u.age,u.version,u.create_time,u.update_time,u.deleted,u.cid from user u
</select>
select="com.gongj.mybatisplus.mapper.CardMapper.selectById"
使用內(nèi)嵌查詢。
調(diào)用
@Test
void joinQuery2(){
List<User> users = userMapper.userJoinCard2();
users.forEach((System.out::println));
}
11却舀、關(guān)聯(lián)查詢并分頁
UserMapper
List<User> userJoinCardPage(Page page);
UserMapper.xml
<select id="userJoinCardPage" resultMap="userJoinCardMap2">
select u.id,u.user_name,u.email,u.age,u.version,u.create_time,u.update_time,u.deleted,u.cid from user u
</select>
測試
@Test
void joinQueryPage(){
Page<User> userPage = new Page<>();
userPage.setSize(1);
userPage.setCurrent(1);
List<User> users = userMapper.userJoinCardPage(userPage);
users.forEach((System.out::println));
}
還是挺簡單的虫几,只需要傳入一個Page對象就可以了。
補充1:
我們發(fā)現(xiàn)如果去調(diào)用單表查詢的結(jié)果禁筏,也就是自帶的查詢持钉,就會出現(xiàn)異常。
@Test
void testSelect(){
// 參數(shù)是一個 Wrapper 篱昔,條件構(gòu)造器每强,這里我們先不用 null 查詢?nèi)坑脩? List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
那怎么解決呢始腾?在實體類的屬性上加入注解,exist 默認為true空执。
@TableField(exist = false)
private List<Card> cards;
注解加在bean屬性上浪箭,表示當前屬性不是數(shù)據(jù)庫的字段,但在項目中必須使用辨绊,這樣在新增奶栖、查詢等使用bean的時候,mybatis-plus就會忽略這個门坷,不會報錯宣鄙。
補充2:多表分頁條件查詢
//多表分頁查詢
@Test
void joinQueryPage3(){
//查詢年齡大于等于20、名稱以j開頭的信息并分頁
Page<User> userPage = new Page<>();
userPage.setSize(2);
userPage.setCurrent(1);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.ge("age",20);
queryWrapper.likeRight("user_name","j");
IPage<User> userIPage = userMapper.userJoinCardPageQuery2(userPage,queryWrapper);
System.out.println("當前頁" + userIPage.getCurrent()); //當前頁
System.out.println("總頁數(shù)" + userIPage.getPages()); //總頁數(shù)
System.out.println("每頁大小" + userIPage.getSize()); //每頁大小
System.out.println("滿足符合條件的條數(shù)" + userIPage.getTotal()); //滿足符合條件的條數(shù)
System.out.println("返回數(shù)據(jù)===="); //返回數(shù)據(jù)
userIPage.getRecords().forEach((System.out::println));
}
執(zhí)行結(jié)果:
當前頁1
總頁數(shù)1
每頁大小2
滿足符合條件的條數(shù)1
返回數(shù)據(jù)====
User(id=2, userName=Jack, age=20, email=test2@baomidou.com, createTime=Sat Oct 31 07:50:19 CST 2020, updateTime=Sat Oct 31 07:50:19 CST 2020, version=0, deleted=0, cid=1, cards=[Card(cid=1, cname=健身卡)])
UserMapper
IPage<User> userJoinCardPageQuery2(Page page, @Param(Constants.WRAPPER)QueryWrapper<User> queryWrapper);
UserMapper.xml
<select id="userJoinCardPageQuery2" resultMap="userJoinCardMap2">
<!--帶上${ew.customSqlSegment 就可以實現(xiàn)查詢-->
select u.id,u.user_name,u.email,u.age,u.version,u.create_time,u.update_time,u.deleted,u.cid from user u
${ew.customSqlSegment}
</select>
執(zhí)行的sql
SELECT COUNT(1) FROM user u WHERE (age >= ? AND user_name LIKE ?)
select u.id,u.user_name,u.email,u.age,u.version,u.create_time,u.update_time,u.deleted,u.cid from user u WHERE (age >= ? AND user_name LIKE ?) LIMIT ?,?
select * from card where cid = ?