fastmybatis開發文檔

簡介

fastmybatis是一個mybatis開發框架,其宗旨為:簡單、快速、有效。

  • 零配置快速上手
  • 無需編寫xml文件即可完成CRUD操作
  • 支持mysql,sqlserver,oracle,postgresql,sqlite
  • 支持自定義sql,sql語句可寫在注解中或xml中
  • 支持與spring-boot集成,依賴starter即可
  • 輕量級,無侵入性,是官方mybatis的一種擴展

快速開始(springboot)

  • 新建一個springboot項目
  • pom.xml添加fastmybatis-spring-boot-starter
<dependency>
    <groupId>net.oschina.durcframework</groupId>
    <artifactId>fastmybatis-spring-boot-starter</artifactId>
    <version>最新版本(見changelog.md)</version>
</dependency>
  • 假設數據庫有張t_user表,添加對應的實體類TUser.java和MapperTUserMapper.java(可用fastmybatis-generator來生成)
  • application.propertis中配置數據庫連接
  • 編寫測試用例
@Autowired
TUserMapper mapper;
    
// 根據主鍵查詢
@Test
public void testGetById() {
    TUser user = mapper.getById(3);
    System.out.println(user);
}

查詢

本小節主要講解fastmybatis的查詢功能。fastmybatis提供豐富的查詢方式,滿足日常查詢所需。

分頁查詢

方式1

前端傳遞兩個分頁參數pageIndex,pageSize

    // http://localhost:8080/page1?pageIndex=1&pageSize=10
    @GetMapping("page1")
    public List<TUser> page1(int pageIndex,int pageSize) {
        Query query = new Query();
        query.page(pageIndex, pageSize);
        List<TUser> list = mapper.list(query);
        return list;
    }

方式2

PageParam里面封裝了pageIndex,pageSize參數

    // http://localhost:8080/page2?pageIndex=1&pageSize=10
    @GetMapping("page2")
    public List<TUser> page2(PageParam param) {
        Query query = param.toQuery();
        List<TUser> list = mapper.list(query);
        return list;
    }

返回結果集和總記錄數

方式1和方式2只能查詢結果集,通常我們查詢還需返回記錄總數并返回給前端,fastmybatis的處理方式如下:

// http://localhost:8080/page3?pageIndex=1&pageSize=10
    @GetMapping("page3")
    public Map<String,Object> page3(PageParam param) {
        Query query = param.toQuery();
        List<TUser> list = mapper.list(query);
        long total = mapper.getCount(query);
        
        Map<String,Object> result = new HashMap<String, Object>();
        result.put("list", list);
        result.put("total", total);
        
        return result;
    }

fastmybatis提供一種更簡潔的方式來處理:

// http://localhost:8080/page4?pageIndex=1&pageSize=10
    @GetMapping("page4")
    public PageInfo<TUser> page4(PageParam param) {
        PageInfo<TUser> pageInfo = MapperUtil.query(mapper, query);
        return result;
    }

PageInfo里面包含了List,total信息,還包含了一些額外信息,完整數據如下:

{
    "currentPageIndex": 1, // 當前頁
    "firstPageIndex": 1, // 首頁
    "lastPageIndex": 2, // 尾頁
    "list": [     // 結果集
        {},
        {}
    ],
    "nextPageIndex": 2, // 下一頁
    "pageCount": 2, // 總頁數
    "pageIndex": 1, // 當前頁
    "pageSize": 10, // 每頁記錄數
    "prePageIndex": 1, // 上一頁
    "start": 0,
    "total": 20 // 總記錄數
}

根據參數字段查詢

查詢姓名為張三的用戶

// http://localhost:8080/sch?username=張三
    @GetMapping("sch")
    public List<TUser> sch(String username) {
        Query query = new Query();
        query.eq("username", username);
        List<TUser> list = mapper.list(query);
        return list;
    }

查詢姓名為張三并且擁有的錢大于100塊

// http://localhost:8080/sch2?username=張三
    @GetMapping("sch2")
    public List<TUser> sch2(String username) {
        Query query = new Query();
        query.eq("username", username).gt("money", 100);
        List<TUser> list = mapper.list(query);
        return list;
    }

查詢姓名為張三并帶分頁

// http://localhost:8080/sch3?username=張三&pageIndex=1&pageSize=5
    @GetMapping("sch3")
    public List<TUser> sch3(String username,PageParam param) {
        Query query = param.toQuery();
        query.eq("username", username);
        List<TUser> list = mapper.list(query);
        return list;
    }

查詢錢最多的前三名

// http://localhost:8080/sch4
    @GetMapping("sch4")
    public List<TUser> sch4() {
        Query query = new Query();
        query.orderby("money", Sort.DESC) // 按金額降序
            .page(1, 3);
        List<TUser> list = mapper.list(query);
        return list;
    }

將參數放在對象中查詢

// http://localhost:8080/sch5?username=張三
    @GetMapping("sch5")
    public List<TUser> sch5(UserParam userParam) {
        Query query = userParam.toQuery();
        query.eq("username", userParam.getUsername());
        List<TUser> list = mapper.list(query);
        return list;
    }

UserParam繼承PageSortParam類,表示支持分頁和排序查詢

使用普通bean查詢

假設有個User類如下

public class User {
    private Integer id;
    private String userName;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

我們將這個類作為查詢參數,那么在springmvc中可以這樣寫:

@GetMapping(path="findUserBean.do")
public List<User> findUser(User user) {
    Query query = Query.build(user);
    List<User> list = dao.find(query);
    return list;
}

Query query = Query.build(user);這句是將User中的屬性轉換成對應條件,假設userName的值為"jim",那么會封裝成一個條件where user_name='jim'

瀏覽器輸入鏈接:http://localhost:8080/fastmybatis-springmvc/findUserBean.do?userName=jim
后臺將會執行如下SQL:

SELECT id,user_name FROM user t WHERE t.user_name = ?

?的值為jim

@Condition注解

@Condition注解用來強化查詢,有了這個注解可以生成各種查詢條件。

@Condition注解有三個屬性:

  • joint:表達式之間的連接符,AND|OR,默認AND
  • column:數據庫字段名,可選
  • operator:連接符枚舉,存放了等于、大于、小于等連接符

如果要查詢id大于2的用戶只需在get方法上加上一個@Condition注解即可:

@Condition(operator=Operator.gt)
public Integer getId() {
    return this.id;
}

這樣,當id有值時,會封裝成一個where id>2的條件

  • 需要注意的是,如果不指定column屬性,系統會默認取get方法中屬性名,然后轉換成數據庫字段名。如果需要指定數據庫字段名的話,可以使用@Condition的column屬性。

public Integer get++UserName++() {
return this.userName;
}

這種情況下會取下劃線部分字段,然后轉換成數據庫字段名。

@Condition(column="username") // 顯示指定字段名
public Integer getUserName() {
    return this.userName;
}

使用@Condition可以生產更加靈活的條件查詢,比如需要查詢日期為2017-12-1~2017-12-10日的記錄,我們可以這樣寫:

@Condition(column="add_date",operator=Operator.ge)
public Date getStartDate() {
    return this.startDate;
}

@Condition(column="add_date",operator=Operator.lt)
public Date getEndDate() {
    return this.endDate;
}

轉換成SQL語句:

t.add_date>='2017-12-1' AND t.add_date<'2017-12-10'

IN查詢

假設前端頁面傳來多個值比如checkbox勾選多個id=[1,2],那么我們在User類里面可以用Integer[]或List<Integer>來接收.

private Integer[] idArr;

public void setIdArr(Integer[] idArr) {this.idArr = idArr;}

@Condition(column="id")
public Integer[] getIdArr() {return this.idArr;}

這樣會生成where id IN(1,2)條件。

排序查詢

// 根據添加時間倒序

Query query = new Query();
query.orderby("create_time",Sort.DESC);
dao.find(query);

多表關聯查詢

多表關聯查詢使用的地方很多,比如需要關聯第二張表,獲取第二張表的幾個字段,然后返回給前端。

fastmybatis的用法如下:

假如我們需要關聯第二張表user_info,篩選出user_info中的城市為杭州的數據。

Query query = new Query()
        // 左連接查詢,主表的alias默認為t
        .join("LEFT JOIN user_info t2 ON t.id = t2.user_id").page(1, 5)
        .eq("t2.city","杭州");

List<TUser> list = mapper.list(query);

System.out.println("==============");
for (TUser user : list) {
    System.out.println(user.getId() + " " + user.getUsername());
}
System.out.println("==============");

多表關聯返回指定字段

有時候不需要全部字段,需要取表1中的幾個字段,然后取表2中的幾個字段,fastmybatis實現方式如下:

Query query = new Query();
// 左連接查詢,主表的alias默認為t
query.join("LEFT JOIN user_info t2 ON t.id = t2.user_id");
// 指定返回字段
List<String> column = Arrays.asList("t2.user_id as userId", "t.username", "t2.city");
// 查詢結果返回到map中
List<Map<String, Object>> mapList = mapper.listMap(column, query);
// 再將map轉換成實體bean
List<UserInfoVo> list = MyBeanUtil.mapListToObjList(mapList, UserInfoVo.class);

執行的SQL語句對應如下:

SELECT t2.user_id as userId , t.username , t2.city
FROM `t_user` t 
LEFT JOIN user_info t2 ON t.id = t2.user_id

使用@Select查詢

@Select注解是mybatis官方提供的一個功能,fastmybatis可以理解為是官方的一種擴展,因此同樣支持此功能。
在Mapper中添加如下代碼:

@Select("select * from t_user where id=#{id}")
TUser selectById(@Param("id") int id);

編寫測試用例

@Test
public void testSelectById() {
    TUser user = dao.selectById(3);

    System.out.println(user.getUsername());
}

對于簡單的SQL,可以用這種方式實現。除了@Select之外,還有@Update,@Insert,@Delete,這里就不多做演示了。

Query類詳解

Query是一個查詢參數類,配合Mapper一起使用。

參數介紹

Query里面封裝了一系列查詢參數,主要分為以下幾類:

  • 分頁參數:設置分頁
  • 排序參數:設置排序字段
  • 條件參數:設置查詢條件
  • 字段參數:可返回指定字段

下面逐個講解每個參數的用法。

分頁參數

一般來說分頁的使用比較簡單,通常是兩個參數,
pageIndex:當前頁索引,pageSize:每頁幾條數據。
Query類使用page(pageIdnex, pageSize)方法來設置。
假如我們要查詢第二頁,每頁10條數據,代碼可以這樣寫:

Query query = new Query();
query.page(2, 10);
List<User> list = dao.find(query);

如果要實現不規則分頁,可以這樣寫:

Query query = new Query();
query.limit(3, 5) // 對應mysql:limit 3,5

排序參數

orderby(String sortname, Sort sort)

其中sortname為數據庫字段,非javaBean屬性

  • orderby(String sortname, Sort sort)則可以指定排序方式,Sort為排序方式枚舉
    假如要按照添加時間倒序,可以這樣寫:
Query query = new Query();
query.orderby("create_time",Sort.DESC);
mapper.list(query);

添加多個排序字段可以在后面追加:

query.orderby("create_time",Sort.DESC).orderby("id",Sort.ASC);

條件參數

條件參數是用的最多一個,因為在查詢中往往需要加入各種條件。
fastmybatis在條件查詢上面做了一些封裝,這里不做太多講解,只講下基本的用法,以后會單獨開一篇文章來介紹。感興趣的同學可以自行查看源碼,也不難理解。

條件參數使用非常簡單,Query對象封裝一系列常用條件查詢。

  • 等值查詢eq(String columnName, Object value),columnName為數據庫字段名,value為查詢的值
    假設我們要查詢姓名為張三的用戶,可以這樣寫:
Query query = new Query();
query.eq("username","張三");
List<User> list = mapper.list(query);

通過方法名即可知道eq表示等于'=',同理lt表示小于<,gt表示大于>

查詢方式 說明
eq 等于=
gt 大于>
lt 小于<
ge 大于等于>=
le 小于等于<=
notEq 不等于<>
like 模糊查詢
in in()查詢
notIn not in()查詢
isNull NULL值查詢
notNull IS NOT NULL
notEmpty 字段不為空,非NULL且有內容
isEmpty 字段為NULL或者為''

如果上述方法還不能滿足查詢需求的話,我們可以使用自定sql的方式來編寫查詢條件,方法為:

Query query = new Query();
query.sql(" username='Jim' OR username='Tom'");

注意:sql()方法不會處理sql注入問題,因此盡量少用。

自定義SQL

方式1

直接寫在Mapper.java中

public interface TUserMapper extends CrudMapper<TUser, Integer> {

    // 自定義sql,官方自帶,不需要寫xml
    /**
     * 修改用戶名
     * @param id
     * @param username
     * @return 返回影響行數
     */
    @Update("update t_user set username = #{username} where id = #{id}")
    int updateById(@Param("id") int id, @Param("username") String username);
 
}

簡單SQL可采用這種形式。

方式2

fastmybatis提供的Mapper已經滿足大部分的操作需求,但是有些復雜的sql語句還是需要寫在xml文件中。fastmybatis同樣支持將sql語句寫在xml中,具體配置如下:

  • 在application.properties添加一句,指定xml文件存放路徑
mybatis.mapper-locations=classpath:/mybatis/mapper/*.xml
  • 在resources/mybatis/mapper目錄下新建一個xml文件TUserMapper.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.mayapp.mapper.TUserMapper">
    
    <select id="selectByName" parameterType="String" resultMap="baseResultMap">
        select * from t_user t where t.username = #{username} limit 1
    </select>
    
</mapper>

這個xml文件跟其它的mybatis配置文件一樣,baseResultMap沒有看到定義,但是確實存在,因為這個是fastmybatis提供的一個內置resultMap。

  • 在TUseroMapper.java中添加:
TUser selectByName(@Param("username")String username);
  • 編寫單元測試用例
@Test
public void testSelectByName() {
    TUser user = dao.selectByName("張三");

    System.out.println(user.getUsername());
}
    

多文件同一個namespace

在以往的開發過程中,一個Mapper對應一個xml文件(namespace)。如果多人同時在一個xml中寫SQL的話會造成各種沖突(雖然能夠最終被解決)。

fastmybatis打破這種常規,允許不同的xml文件定義相同的namespace,程序啟動時會自動把他們的內容合并到同一個文件當中去。

  • 張三的UserMapper_zs.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.mayapp.mapper.TUserMapper">
    
    <select id="selectByName" parameterType="String" resultMap="baseResultMap">
        select * from t_user t where t.username = #{username} limit 1
    </select>
    
</mapper>
  • 李四的UserMapper_ls.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.mayapp.mapper.TUserMapper">
    
    <select id="updateUser" parameterType="String" resultMap="baseResultMap">
        update t_user set username = #{username} where id=#{id}
    </select>
    
</mapper>

最終會合并成

<?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.mayapp.mapper.TUserMapper">
    <!-- 張三部分 -->
    <select id="selectByName" parameterType="String" resultMap="baseResultMap">
        select * from t_user t where t.username = #{username} limit 1
    </select>
    
    <!-- 李四部分 -->
    <select id="updateUser" parameterType="String" resultMap="baseResultMap">
        update t_user set username = #{username} where id=#{id}
    </select>
    
</mapper>

這樣也體現了開閉原則,即新增一個功能只需要新增一個文件就行,不需要修改原來的文件。

如果SQL寫多了還可以把它們進行分類,放到不同的xml中,便于管理。

注:合并動作是在啟動時進行的,并不會生成一個真實的文件。

字段自動填充

填充器設置

假設數據庫表里面有兩個時間字段gmt_create,gmt_update。

當進行insert操作時gmt_create,gmt_update字段需要更新。當update時,gmt_update字段需要更新。

通常的做法是通過Entity手動設置:

User user = new User();
user.setGmtCreate(new Date());
user.setGmtUpdate(new Date());

因為表設計的時候大部分都有這兩個字段,所以對每張表都進行手動設置的話很容易錯加、漏加。
fastmybatis提供了兩個輔助類DateFillInsert和DateFillUpdate,用來處理添加修改時的時間字段自動填充。配置了這兩個類之后,時間字段將會自動設置。

配置方式如下:

EasymybatisConfig config = new EasymybatisConfig();

    config.setFills(Arrays.asList(
            new DateFillInsert()
            ,new DateFillUpdate()
            ));

在spring的xml中配置如下:

<bean id="sqlSessionFactory"
        class="com.gitee.fastmybatis.core.ext.SqlSessionFactoryBeanExt">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation">
            <value>classpath:mybatis/mybatisConfig.xml</value>
        </property>
        <property name="mapperLocations">
            <list>
                <value>classpath:mybatis/mapper/*.xml</value>
            </list>
        </property>
        
        <!-- 以下是附加屬性 -->
        
        <!-- dao所在的包名,跟MapperScannerConfigurer的basePackage一致 
            多個用;隔開
        -->
        <property name="basePackage" value="com.myapp.dao" />
        <property name="config">
            <bean class="com.gitee.fastmybatis.core.EasymybatisConfig">
                                <!-- 定義填充器 -->
                <property name="fills">
                    <list>
                                        <bean class="com.gitee.fastmybatis.core.support.DateFillInsert"/>
                                        <bean class="com.gitee.fastmybatis.core.support.DateFillUpdate"/>
                    </list>
                </property>
            </bean>
        </property>
    </bean>

springboot中可以這樣定義:

在application.properties中添加:

mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=
mybatis.fill.com.gitee.fastmybatis.core.support.DateFillUpdate=

如果要指定字段名,可以寫成:

mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=add_time

自定義填充器

除了使用fastmybatis默認提供的填充之外,我們還可以自定義填充。

自定義填充類要繼承FillHandler<T>類。
<T> 表示填充字段類型,如Date,String,BigDecimal,Boolean。

實戰(springboot)

現在有個remark字段,需要在insert時初始化為“備注默認內容”,新建一個StringRemarkFill類如下:

public class StringRemarkFill extends FillHandler<String> {

    @Override
    public String getColumnName() {
        return "remark";
    }

    @Override
    public FillType getFillType() {
        return FillType.INSERT;
    }

    @Override
    protected Object getFillValue(String defaultValue) {
        return "備注默認內容";
    }

}

StringRemarkFill類中有三個重寫方法:

  • getColumnName() : 指定表字段名
  • getFillType() : 填充方式,FillType.INSERT:僅insert時填充; FillType.UPDATE:insert,update時填充
  • getFillValue(String defaultValue) :返回填充內容

然后在application.properties中添加:

mybatis.fill.com.xx.StringRemarkFill=

這樣就配置完畢了,調用dao.save(user);時會自動填充remark字段。

指定目標類

上面說到StringRemarkFill填充器,它作用在所有實體類上,也就是說實體類如果有remark字段都會自動填充。這樣顯然是不合理的,解決辦法是指定特定的實體類。只要重寫FillHandler類的getTargetEntityClasses()方法即可。

@Override
public Class<?>[] getTargetEntityClasses() {
    return new Class<?>[] { TUser.class };
}

這樣就表示作用在TUser類上,多個類可以追加。最終代碼如下:

public class StringRemarkFill extends FillHandler<String> {

    @Override
    public String getColumnName() {
        return "remark";
    }

    @Override
    public Class<?>[] getTargetEntityClasses() {
        return new Class<?>[] { TUser.class }; // 只作用在TUser類上
    }

    @Override
    public FillType getFillType() {
        return FillType.INSERT;
    }

    @Override
    protected Object getFillValue(String defaultValue) {
        return "備注默認內容"; // insert時填充的內容
    }

}

關于自動填充的原理是基于mybatis的TypeHandler實現的,這里就不多做介紹了。感興趣的同學可以查看FillHandler<T>源碼。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容