MyBatis學習簡單總結

前言

利用端午時間簡單學習了MyBatis,現根據學習內容進行簡單總結。

知識點

1. MyBatis框架簡介

MyBatis是一個持久化框架,使用Java編寫,封裝了JDBC的很多實現細節,可以使開發者只關注SQL語句,而無需關注註冊驅動/建立連接等繁雜的過程,封裝過程使用ORM思想來實現。

ORM:Object Relational Mapping 對象關係映射,把數據庫表和實體類的屬性對應起來,使得可以通過操作實體類來實現數據庫表

2. 基於XML和基於註解的開發實例

以User爲實體類,構建簡單的一個入門實例,通過MyBatis操作實體類完成對數據庫user表的CRUD操作,之後分別會用基於XML和基於註解的方式完成。

2.1 建表

通過Navicat連接MySQL完成建表操作。

2.1.1 啓動mysql服務【管理員模式打開cmd】

2.1.2 創建User表並插入數據

2.2 導入依賴

創建maven項目,導入mybatis/mysql-connection等依賴

    <dependencies>
        <!-- MyBatis依賴 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

        <!-- mysql依賴 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

        <!-- log4j依賴 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.12.1</version>
        </dependency>

        <!-- 單元測試依賴 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.3 創建User實體類/IUserDao接口

2.3.1 根據數據庫字段創建User實體類並定義屬性和getter/setter方法

/**
 * User實體類
 */
public class User implements Serializable {
    private Long userId;
    private String username;
    private String password;
    private Date birthday;
    private String sex;
    private String address;

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

2.3.1 創建User接口類,並根據查詢所有用戶功能創建抽象方法

public interface IUserDao {
    /**
     * 查詢所有用戶
     * @return
     */
    List<User> findAllUsers(); //public abstract已省略
}

2.4 添加映射配置文件

配置文件主要是配置數據庫連接信息以及爲方便給類名起別名,配置映射文件的路徑

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--MyBatis的主配置文件-->
<configuration>
    <!--配置環境-->
    <environments default="mysql">
        <!--配置mysql環境-->
        <environment id="mysql">
            <!--配置事務的類型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置數據源/連接池-->
            <dataSource type="POOLED">
                <!--配置連接數據庫的4個基本信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_test?characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--指定映射配置文件的位置,映射配置文件指的是每個dao獨立的配置文件-->
    <mappers>
        <mapper resource="com/practice/dao/IUserDao.xml"/>
    </mappers>
</configuration>

2.5 基於XML模式的映射文件及查詢所有用戶功能測試

基於XML模式主要是映射配置文件中mapper中會指定resource爲映射文件的全限定路徑,另外根據IUserDao接口的方法編寫映射文件。

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace鎖定接口 -->
<mapper namespace="com.practice.dao.IUserDao"> 
    <!-- id鎖定方法,resultType爲方法返回類型,另外還有parameterType表示方法參數類型 -->
    <select id="findAllUsers" resultType="com.practice.domain.User">
        select * from user
    </select>
</mapper>
public class IUserTest {

    private InputStream in;
    private SqlSession session;
    private IUserDao userDao;

    /**
     * 初始化操作
     */
    @Before   //每一個測試之前都需要初始化
    public void init() throws IOException {
        //1.讀取映射配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.構建SqlSessionFactory工廠對象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.構建SqlSession會話對象
        session = factory.openSession();
        //4.構建代理IUserDao對象
        userDao = session.getMapper(IUserDao.class);
    }

    /**
     * 測試查詢所有用戶
     */
    @Test
    public void testFindAllUsers(){
        List<User> users = userDao.findAllUsers();
        for(User user : users){
            System.out.println(user);
        }
    }

    @After //在每一個測試之後都需要釋放資源
    public void destroy() throws IOException {
        //釋放資源
        session.close();
        in.close();
    }
}

測試結果即查詢到所有的用戶信息。

2.6 基於註解的接口及查詢所有用戶功能測試

基於註解主要是不使用映射文件,映射配置文件中的mapper使用class屬性,值爲接口的全限定路徑。

public interface IUserDao {
    /**
     * 查詢所有用戶
     * @return
     */
    @Select("select * from user") //通過此註解完成對數據庫的操作。package+方法名對比xml中的namespace,接口方法對比xml中的id,註解中的sql對比xml中的sql,方法的參數和返回值對比xml中的parameterType和resultType
    List<User> findAllUsers();
}

基於註解的方法測試同基於xml的方法測試,測試結果相同。後續其他CRUD功能都會分別以基於XML和基於註解進行說明。

2.7 一般開發中使用上述中的代理模式進行開發,但是也可以通過daoImpl實現,此處以基於xml方式+daoimpl進行簡單說明一下

創建實現IUserDao的實現類IUserDaoImpl,並在方法中創建SqlSession。調用SqlSession的selectList方法,參數爲namespace+id。SqlSession還支持insert/selectOne/delete/update方法, 參數也可以加Object對象(例如insert的參數爲路徑和user對象)

public class IUserDaoImpl implements IUserDao {
    private SqlSessionFactory sessionFactory;

    /**
     * 通過構造函數保證SqlSessionFactory不爲空
     * @param sqlSessionFactory
     */
    public IUserDaoImpl(SqlSessionFactory sqlSessionFactory){
        this.sessionFactory = sqlSessionFactory;
    }

    public List<User> findAllUsers() {
        //1.使用工廠創建SqlSession對象
        SqlSession session = sessionFactory.openSession();
        //2.使用session執行查詢所有方法
        List<User> users = session.selectList("com.practice.dao.IUserDao.findAllUsers");
        //釋放資源
        session.close();
        //3.返回查詢結果
        return users;
    }
}

由於這種開發比較繁瑣,此處只是簡單說明。目前用到的基本都是基於xml的開發模式。

2.8 typeAliases與package的使用

在使用中可能會有多個實體類,爲了xml中參數方便不再重複寫全限定路徑,可以中typeAiases標籤在映射配置文件中進行設置。

如果實體類比較多的話,可以使用package標籤。

另外,映射配置文件中mapper標籤也支持package

3. MyBatis的基本操作

主要是記錄xml和註解的表達,測試和上述測試類似,差別的地方在於調用接口的不同方法,不再贅述。xml和註解兩種開發不可並存,否則運行會報錯(即不能有註解之後還有xml映射文件)

3.1 根據ID查詢用戶

3.1.1 基於xml

<!-- 根據用戶ID查詢用戶 -->
<select id="findById" parameterType="LONG" resultType="com.practice.domain.User">
    select * from user
    where userId=#{userId}
</select>

3.1.2 基於註解

@Select("select * from user where userId=#{userId}")
@ResultMap(value = {"userMap"})
User findByUserId(Long userId);

3.2 根據用戶名模糊查詢用戶

3.2.1 基於XML

<!-- 根據用戶名模糊查詢用戶 -->
    <!-- preparedStatement對象 -->
    <select id="findByName" parameterType="java.lang.String" resultType="com.practice.domain.User">
        select * from user
        where username like #{username}
    </select>
    <!-- statement對象 -->
<!--    <select id="findByName" parameterType="java.lang.String" resultType="com.practice.domain.User">-->
<!--        select * from user-->
<!--        where username like '%${value}%'-->
<!--    </select>-->

3.2.2 基於註解

@Select("select * from user where username like #{username}") //preparedStatement,較爲常用
//    @Select("select * from user where username like '%${value}%' ") //statement
    @ResultMap("userMap")
    List<User> findByUsername(String username);

3.3 增加用戶

3.3.1 基於XML

<!-- 添加一個用戶 -->
    <insert id="saveUser" parameterType="USER">
    <!-- 配置插入操作後,返回插入數據的ID -->
    <selectKey keyProperty="userId" keyColumn="userId" resultType="LONG" order="AFTER">
        select last_insert_id();
    </selectKey>
        insert into user(username,password,birthday,sex,address)
        values(#{username},#{password},#{birthday},#{sex},#{address})
    </insert>

3.3.2 基於註解

@Insert("insert into user(username,password,birthday,sex,address) " +
        "values(#{username},#{password},#{birthday},#{sex},#{address})")
void saveUser(User user);

3.4 更新用戶

3.4.1 基於XML

<!-- 更新一個用戶 -->
<update id="updateUser" parameterType="uSer">
    update user set
    username=#{username},password=#{password},birthday=#{birthday},sex=#{sex},address=#{address}
    where userId=#{userId}
</update>

3.4.2 基於註解

@Update("update user set username=#{username},password=#{password}," +
        "birthday=#{birthday},sex=#{sex},address=#{address} where userId=#{userId}")
void updateUser(User user);

3.5 刪除用戶

3.5.1 基於XML

<!-- 刪除一個用戶 #{佔位符是可以隨意定義的}-->
<delete id="deleteUser" parameterType="java.lang.Long">
    delete from user
    where userId=#{userId}
</delete>

3.5.2 基於註解

@Delete("delete from user where userId=#{userId}")
void deleteUser(Long userId);

3.6 resultMap的使用

一般開發中會遇到實體類的屬性名和數據庫字段名不一致的情況,可以通過ResultMap進行映射匹配,例如數據庫中user_id對應實體類中的userId,分別基於xml和基於註解進行屬性映射

3.6.1 基於XML

 <!-- 配置 查詢結果的列名和實體類屬性名的對應關係 -->
    <resultMap id="userMap" type="com.practice.domain.User">
        <!-- 主鍵字段的對應 -->
        <id property="user_id" column="user_id"></id>
        <!-- 非主鍵字段的對應 -->
        <result property="userName" column="username"></result>
        <result property="password" column="password"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
    </resultMap>

<!-- 使用提前配置數據庫字段與實體類屬性名的對應關係後,此處resultType需要改成userMap(即導入對應關係配置)
        parameterMap同理,如果參數爲實體類的話需要使用parameterMap="userMap"-->
    <select id="findAllUsers" resultMap="userMap">
        select * from user
    </select>

3.6.2 基於註解

@Select("select * from user")
    @Results(id="userMap", //聲明id之後其他使用該映射規則的直接使用resultMap,然後寫入這個id即可
            value = {@Result(id = true,column = "user_id",property = "userId"),
                    @Result(column = "username",property = "username"), //非主鍵屬性,id默認爲false
                    @Result(column = "password",property = "password"),
                    @Result(column = "birthday",property = "birthday"),
                    @Result(column = "sex",property = "sex"),
                    @Result(column = "address",property = "address")}
    )
    List<User> findAllUsers();

4. MyBatis的多表查詢操作

4.1 多對一:例如多個賬戶可以隸屬於同一個用戶

在MyBatis中多對一可以看作一對一,對於一個賬戶而言,只有一個用戶。實現功能:查詢所有的賬戶並返回賬戶對應的用戶信息,通過關聯屬性

4.1.1 在賬戶Account實體類中建立關聯屬性User

4.1.2 基於xml實現關聯

<resultMap id="accountUserMap" type="com.practice.domain.Account">
        <id property="accountId" column="accountId"></id>
        <result property="userId" column="userId"></result>
        <result property="money" column="money"/>
        <!-- 一對一的關係映射,配置封裝user的內容 -->
        <association property="user" column="userId" javaType="com.practice.domain.User">
            <id property="userId" column="userId"/>
            <result property="username" column="username"/>
            <result property="password" column="password"/>
            <result property="birthday" column="birthday"/>
            <result property="sex" column="sex"/>
            <result property="address" column="address"/>
        </association>
    </resultMap>

4.1 基於註解實現關聯

@Select("select * from account")
    @Results(id = "accountMap", value = {
            @Result(id=true,column = "accountId",property = "accountId"),
            @Result(column = "userId",property = "userId"),
            @Result(column = "money",property = "money"),
            @Result(property = "user",column = "userId",
                    one = @One(select="com.practice.dao.IUserDao.findByUserId"))
    })
    List<Account> findAllAccounts();

4.2 一對多:例如一個用戶可以擁有多個賬戶

實現功能:查詢所有用戶並返回用戶對應的所有賬戶信息,通過集合屬性

4.2.1 User實體類中創建集合屬性Account

4.2.2 基於xml實現集合

<!-- 定義User的resultMap -->
    <resultMap id="userMap" type="com.practice.domain.User">
        <id property="userId" column="userId"></id>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
        <collection property="roles" ofType="role">
            <id property="roleId" column="roleId"></id>
            <result property="rolename" column="rolename"></result>
        </collection>
    </resultMap>

4.2.3 基於註解實現集合

@Select("select * from user")
    @Results(id="userMap",
            value = {@Result(id = true,column = "userId",property = "userId"),
                    @Result(column = "username",property = "username"),
                    @Result(column = "password",property = "password"),
                    @Result(column = "birthday",property = "birthday"),
                    @Result(column = "sex",property = "sex"),
                    @Result(column = "address",property = "address"),
            @Result(property = "accounts",column = "userId",
                    many = @Many(select="com.practice.dao.IAccountDao.findByUserId"))} 
    )
    List<User> findAllUsers();

查詢的時候直接調用User對象的getAccounts方法即可。

4.3 多對多:可以劃分爲兩個一對多關係,分別使用集合屬性完成,此處不再贅述

5. MyBatis中的連接池

MyBatis支持三種數據庫連接方式,POOLED/UNPOOLED/JNDI

5.1 POOLED

採用傳統的java.sql.DataSource規範中的連接池,MyBatis中有針對規範的實現

5.2 UNPOOLED

採用傳統的獲取連接的方式,雖然也實現java.sql.DataSource接口,但是並沒有使用池的思想,只是單純建立數據庫連接

5.3 JNDI

採用服務器提供的JNDI技術來獲取DataSource對象,不同的服務器所能拿到的DataSource不同。如果不是web/maven的war工程是不能使用的,tomcat服務器中採用連接池爲dbcp連接池

6. MyBatis中的加載

6.1 立即加載

不管用不用,只要一調用方法,馬上發起查詢。一般一對一使用立即加載。

6.1.1 基於XML的立即加載

<resultMap id="accountUserMap" type="com.practice.domain.Account">
        <id property="accountId" column="accountId"></id>
        <result property="userId" column="userId"></result>
        <result property="money" column="money"/>
        <!-- select屬性指定的內容:查詢用戶的唯一標識
             column屬性指定的內容:用戶根據id查詢時所需要的參數值-->
        <association property="user" column="userId" javaType="user"
                     select="com.practice.dao.IUserDao.findById"></association>
    </resultMap>
    <select id="findAllAccounts" resultMap="accountUserMap">
        select * from account
    </select>

6.1.2 基於註解的立即加載,FetchType爲EAGER

@Select("select * from account")
    @Results(id = "accountMap", value = {
            @Result(id=true,column = "accountId",property = "accountId"),
            @Result(column = "userId",property = "userId"),
            @Result(column = "money",property = "money"),
            @Result(property = "user",column = "userId",
                    one = @One(select="com.practice.dao.IUserDao.findByUserId",fetchType = FetchType.EAGER))
    })
    List<Account> findAllAccounts();

6.2 延遲加載

在真正使用數據時才發起查詢,不用的時候不查詢,按需加載(懶加載)。一般一對多使用延遲加載

無論是xml還是註解,都需要在映射配置文件中設置開啓延遲加載。

6.2.1 基於XML的延遲加載

<!-- 定義User的resultMap -->
    <resultMap id="userMap" type="com.practice.domain.User">
        <id property="userId" column="userId"></id>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
        <collection property="account" ofType="account"
                    select="com.practice.dao.IAccountDao.findAccountByUserId" column="userId"></collection>
    </resultMap>

    <select id="findAllUsers" resultMap="userMap">
        select * from user
    </select>
<!-- 查詢用戶直接通過這個select查,如果需要查詢賬戶,則使用userId作爲參數去IAccountDao的映射文件中去找findAccountByUserId去查 -->

6.2.2 基於註解的延遲加載,FetchType爲LAZY

@Select("select * from user")
    @Results(id="userMap",
            value = {@Result(id = true,column = "userId",property = "userId"),
                    @Result(column = "username",property = "username"),
                    @Result(column = "password",property = "password"),
                    @Result(column = "birthday",property = "birthday"),
                    @Result(column = "sex",property = "sex"),
                    @Result(column = "address",property = "address"),
            @Result(property = "accounts",column = "userId",
                    many = @Many(select="com.practice.dao.IAccountDao.findByUserId",fetchType = FetchType.LAZY))}
    )
    List<User> findAllUsers();

7. MyBatis中的緩存

7.1 一級緩存

MyBatis中SqlSession對象的緩存。當執行查詢之後,查詢的結果會同時存入到SqlSession提供的一塊區域中,區域的結構是一個Map。當再次查詢相同數據時MyBatis會先去SqlSession中查詢是否有,有的話會直接拿來用。SqlSession消失後這個緩存Map也會隨之消失

7.2 二級緩存

MyBatis中SqlSessionFactory對象的緩存,由同一個SqlSessionFactory對象創建的SqlSession共享其緩存。

7.2.1 基於XML開發中二級緩存的開啓方式

1. 在映射配置文件配置讓MyBatis支持二級緩存

2. 在映射文件中配置

3. 在具體的方法中配置

7.2.2 基於註解開發中二級緩存的開啓方式

1. 在映射配置文件中配置

2. 在接口類中使用註解進行配置

總結

千里之行始於足下,積少成多,認真踏實,好好學習,努力進步。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章