天天看點

持久層架構學習——MyBatis(一)

持久層架構學習——MyBatis(一)

      • 使用原始JDBC操作資料庫的缺點
      • MyBatis介紹
      • MyBatis簡單使用
      • MyBatis配置檔案
        • 映射檔案
        • 核心配置檔案
          • 配置檔案中标簽的層級關系
          • 常用标簽配置
            • environments 标簽
            • mapper 标簽
            • Properties 标簽
            • typeAliases 标簽
      • MyBatis的簡單增删改查
        • MyBatis的添加操作
        • MyBatis的删除操作
        • MyBatis的修改操作
        • MyBatis的查詢操作
      • MyBatis相關API
      • MyBaits的DAO層的實作方式
        • 傳統方式
        • 接口代理方式

使用原始JDBC操作資料庫的缺點

  1. 資料庫的連接配接建立和釋放的頻繁造成系統資源浪費影響系統的性能。
  2. 使用PreparedStatement時SQL語句中的占位符傳參存在寫死,造成代碼不易維護,若改動SQL語句還需要改動Java代碼。
  3. 查詢操作時,需要手動将結果集封裝在實體中;插入操作時,需要手動将實體資料設定到SQL語句的占位符位置。如果進行修改需要對大量的代碼進行修改,不利于維護。

解決設想:

  1. 使用資料庫連接配接池管理資料庫的連接配接。
  2. 将相關的SQL語句提取出來,在配置檔案中進行配置,此時在進行修改時隻需要修改配置檔案即可。
  3. 可以使用反射、内省等底層技術,自動将實體與表進行屬性與字段的字段映射。

MyBatis介紹

MyBatis是Java開發中的一個持久層架構,它内部封裝了JDBC,這樣使得我們隻需要寫SQL語句,不用去管加載驅動、建立連接配接、建立Statement等過程。MyBatis通過XML或注解的方式将要執行的各種Statement配置起來,并通過Java對象和Statement中SQL的動态參數進行映射生成最終執行的SQL語句。最後MyBatis架構執行SQL後将結果映射成Java對象傳回。采用了ORM(對象關系映射)的思想解決了實體和資料庫映射的問題,對JDBC進行了封裝,屏蔽JDBC API底層通路的細節,使得我們不用去處理原生JDBC就能對資料庫進行持久化的操作。

MyBatis簡單使用

步驟:

①導入MyBatis的坐标。–>②建立一個資料表。–>③編寫資料表字段對應的實體類。–>④編寫映射檔案

XxxMapper.xml

XxxMapping.xml

。–>⑤編寫核心檔案

SqlMapConfig.xml

。–>⑥測試

第一步:導入MyBatis坐标,這裡還是采用Maven進行項目依賴管理,是以不需要手動去導包。如果不用Maven,可以在mybatis官網下載下傳相應的jar包。做法如下:

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.32</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <!--    引入MyBatis    -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.6</version>
    </dependency>
</dependencies>
           

第二步:建立一個資料表。這個可以在自己的資料庫中采用任何一個表都行。

第三步:編寫資料表字段對應的實體類。這一步就是之前建立實體對象中的建立實體類,根據表的設計進行相應的建立即可。

第四步:編寫映射檔案

UserMapping.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="userMapping">
    <select id="findAll" resultType="com.jclight.domain.User">
        select * from user
    </select>
</mapper>
           

第五步:編寫核心檔案

SqlMapConfig.xml

。具體如下:

<?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">
<configuration>
    <!--  資料源環境  -->
    <environments default="Developement">
        <environment id="Developement">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
           

第六步:測試

MyBatis配置檔案

在持久層使用MyBatis架構開發時,我們需要對MyBatis的兩大配置檔案進行配置才可以使用,這兩大就是映射檔案和核心配置檔案。

映射檔案

關于映射檔案的命名,我們對哪個實體進行操作就使用

實體名+Mapper/Mapping.xml

進行命名,例如

UserMapper.xml、ProductMapping.xml

。關于映射檔案的部分代碼介紹如下:

這個部分是映射檔案DTD的限制頭,它的作用可以幫助我們提示映射檔案的編寫和限制,DTD檔案頭就是一種限制,隻要檔案中有DTD限制頭,編譯器或浏覽器就能根據該限制進行相應的操作,這裡的指定限制是mapper,一個規範的HTML檔案中,它的限制頭指定的就是HTML。當不指定限制頭或不編寫限制頭時,浏覽器就不能顯示出我們檔案中的所有代碼的頁面展示。或者編譯器就不能對該檔案進行準确的識别。

該标簽是映射檔案中的根标簽,屬性namespace是命名空間,和下面不同的SQL标簽的id組成辨別。這裡的namespace的值一般來講要寫成包的形式,但這種不是固定死的,一般要根據項目的結構來填寫這裡的值。

<select id="findAll" resultType="com.jclight.domain.User">
	select * from user
</select>
           

該标簽是SQL标簽,除此之外還有insert、update、delete。這裡的id與上面的namespace對應組成辨別,resultType結果類型,一般對應的是實體類型,它的值是對應實體類型的全包名,指定全包名後架構就會使用反射機制來将我們的查詢結果自動封裝成我們想要的對象了。在該标簽的内部編寫要執行的SQL語句。

核心配置檔案

配置檔案中标簽的層級關系
常用标簽配置

在MyBaits的核心配置檔案中,前兩行是檔案的限制,這個限制和Mapper檔案的限制不一樣,如下所示:

<?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">
<configuration>
</configuration>
           

後面的兩行,也就是

<configuration>

标簽是核心配置檔案的根标簽,在根标簽下又有很多标簽代表了不同的含義,具體如層級關系所示。

environments 标簽

<environments>

标簽是資料庫環境的配置,它支援多環境的配置,它的屬性default代表指定預設的環境名稱。在其下又有

<environment>

标簽,它有屬性id代表指定目前環境的名稱。是以在标簽environments下可以配置多種環境,指定預設的環境隻需要屬性default的值和id的值互相對應即可。

在environment标簽下又有

<transactionManager>

标簽,它的作用是指定事務管理,屬性type就是指定事務管理的類型是什麼,例如JDBC等。這裡的type的類型有兩種,一種是JDBC,使用JDBC作為事務管理器就是直接使用了JDBC的送出和復原設定,它依賴于從資料源得到的連接配接來管理事務作用域。另一種是MANAGED,使用這種不會送出和復原連接配接,而是讓容器來管理事務的整個生命周期。預設下會關閉連接配接,然而有些容器不希望如此,是以可以通過設定closeConnection屬性的值為false來阻止它預設關閉的行為。

<environment>

标簽下還有的

<dataSource>

标簽,它的作用是指定目前的資料源的類型,屬性type和上面一樣是指定目前的資料源類型,例如POOLED表示資料源類型是連接配接池。這裡的type有三種,其中UNPOOLED值表示資料源的實作隻是在每次被請求時打開或關閉連接配接。POOLED表示資料源實作利用池的概念将JDBC對象組織連起來。JNDI屬性表示實作能在如EJB應用中或應用伺服器這類容器中使用,容器可以集中或在外部配資料源,然後放在一個JNDI上下文的引用。

mapper 标簽

該标簽的作用是加載映射的,它的位置在标簽mappers内部,加載映射方式有如下的幾種:

  1. 使用相對于類路徑的資源的引用,例如:
  2. 使用完全限定資源定位符URL,例如:
  3. 使用映射器接口實作類的完全限定類名,例如:
  4. 将包内的映射器接口實作全部注冊為映射器,例如:

Properties 标簽

該标簽的作用和Spring中導入外部資源檔案一樣,它也是通過該标簽進行導入,然後在MyBatis的核心配置檔案中使用MyBatis提供的表達式進行提取。例如:

<!--  導入外部資源檔案  -->
<properties resource="jdbc.properties"/>  
<!--  資料源環境  -->
<environments default="Developement">
    <environment id="Developement">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
</environments>
           

typeAliases 标簽

它的作用是為類型别名設定一個簡短的名字。例如:

<typeAliases>
     <typeAlias type="com.jclight.domain.User" alias="user"/>
</typeAliases>
           

此時在映射檔案中,可以修改為:

<select id="findAll" resultType="user">
        select * from user;
</select>
           

注意:該标簽在核心配置檔案中有位置規定,必須編寫在合适的位置之後才能使用,一般來說觀察IDEA所提示的錯誤進行位置的修改即可。此外,MyBatis将我們常用的基本資料類型及其對應的包裝類也做了相應的别名,例如java.lang.Integer可以省略為int等。

MyBatis的簡單增删改查

MyBatis的添加操作

<?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="UserMapper">
    <!--  插入操作  -->
    <insert id="save" parameterType="com.jclight.domain.User">
        insert into user values(#{id},#{username},#{password});
    </insert>
</mapper>
           

注意:這裡的SQL标簽為insert,屬性parameterType是輸入類型,這裡根據資料表填寫相應的輸入類型。SQL語句中原有的占位符

?

在這裡是

#{}

,括号中的值對應實體的屬性名。

測試如下:

@Test
public void Insert() throws IOException {
    User user = new User();
    user.setUsername("尼古拉斯趙四");
    user.setPassword("aaa");
    
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    sqlSession.insert("UserMapper.save", user);
    //MyBatis執行更新操作 送出事務
    sqlSession.commit();
    sqlSession.close();
}
           

這裡是利用MyBatis的API進行的資料庫操作,是以是一個簡單的單元測試,便于學習,但是在開發中是不存在這樣的寫法的,請注意。

注意:MyBatis執行更新操作(增删改)時,資料庫事務預設是不送出的,是以在我們進行測試的時候需要送出事務。

MyBatis的删除操作

映射檔案如下:

<?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="UserMapper">
	<!--  删除操作  -->
    <delete id="delete" parameterType="java.lang.Integer">
        delete from user where id=#{id};
    </delete>
</mapper>
           

測試如下:

@Test
public void DeleteUser() throws IOException {
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    sqlSession.delete("UserMapper.delete", 12);
    //MyBatis執行更新操作 送出事務
    sqlSession.commit();
    sqlSession.close();
}
           

注意:如果要操作資料庫表中的單個資料,也就是在映射檔案中的SQL語句中隻有一個使用占位符的參數時,此時在MyBatis的增删改API中的傳遞的參數的要對應

parameterType

屬性值所寫的類型。而在映射檔案中的占位符中的名稱可以為任何值。如果操作的多個資料,就不行了。除此之外,也要看

parameterType

的值是什麼,如果是基本資料類型且占位符隻有一個時,可以使用任何值作為占位符中的填充值,如果不是基本資料類型且占位符隻有一個,這時就需要使用實體對象對應的屬性值的名稱。

MyBatis的修改操作

映射檔案如下:

<?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="UserMapper">
	<!--  修改操作  -->
    <update id="update" parameterType="com.jclight.domain.User">
        update user set username=#{username},password=#{password} where id=#{id};
    </update>
</mapper>
           

測試如下:

@Test
public void UpdateUser() throws IOException {
    User user = new User();
    user.setUsername("尼古拉斯趙四");
    user.setPassword("aaa");
    
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    sqlSession.insert("UserMapper.save", user);
    //MyBatis執行更新操作 送出事務
    sqlSession.commit();
    sqlSession.close();
}
           

注意:此時用到的MyBatis的API是insert。插入與修改操作的API都含有兩個參數,第一個參數對應的映射檔案的

namespace.id

組合,第二個參數是傳入映射檔案的操作對象。

MyBatis的查詢操作

映射檔案如下:

<?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="userMapping">
    <select id="findAll" resultType="com.jclight.domain.User">
        select * from user
    </select>
</mapper>
           

測試如下:

public class MyBatisTest {
    @Test
    public void test1() throws IOException {
        //獲得核心配置檔案
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        //獲得session工程對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //獲得session會話對象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //執行操作 參數:namespace.id
        List<User> userList = sqlSession.selectList("userMapping.findAll");
        //展示資料
        System.out.println(userList);
        //釋放資源
        sqlSession.close();
    }
}
           

注意:上述的測試代碼是使用MyBatis提供的相關API進行的操作,除此之外我們可以使用Spring來內建MyBatis進而簡化上述的代碼。

MyBatis相關API

MyBatis相關的API如下:

API 描述
SqlSessionFactory build(InputStream inputStream) 通過加載mybatis的核心檔案,以輸入流的形式建構一個SqlSessionFactory對象
SqlSession openSession() 預設開啟一個事務,但事務不會自動送出,是以需要手動去送出事務,送出後更新操作資料才會持久化到資料庫中
SqlSession openSession(boolean autoCommit) 參數為是否自動送出,若為true則不需要手動送出事務

上表中的第一個API其實就是使用SqlSession工廠構造器SqlSessionFactoryBuilder來得到一個SqlSessionFactory,例如:

String resource = "SqlMapConfig.xml"; 
InputStream inputStream = Resources.getResourceAsStream(resource); 
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); 
SqlSessionFactory factory = builder.build(inputStream);
           

這裡面的

Resources

是一個工具類,該類可以幫助我們從類路徑下、檔案系統或WEB URL中加載資源檔案。

第二個和第三個API都是使用SqlSession工廠對象SqlSessionFactory建立SqlSession的執行個體的。

其中最重要的一個對象就是SqlSession對象,主要有兩方面的作用,一是幫助我們執行語句的操作,二是幫助我們操作事務,它的相關API如下:

API

<T> T selectOne(String statement, Object parameter)

<E> List<E> selectList(String statement, Object parameter)

int insert(String statement, Object parameter)

int update(String statement, Object parameter)

int delete(String statement, Object parameter)

void commit()

void rollback()

其中前五個是用來執行語句(查增改删)的操作,後兩個是送出事務和復原事務的操作。

MyBaits的DAO層的實作方式

傳統方式

傳統方式的步驟就是先建立對應的實體Mapper接口,在其中建立需要的操作資料庫的方法,之後建立該接口的實作,建立完成後,實作接口下的方法,最後進行測試即可。這裡的三個配置檔案不變,和上述一緻。接口如下:

public interface UserMapper {
    /**
     * 在User表中查詢全部資料
     *
     * @return User對象組成的集合
     * @throws IOException 将IO異常抛出
     */
    List<User> findAll() throws IOException;
}
           

實作類如下:

public class UserMapperImpl implements UserMapper {
    @Override
    public List<User> findAll() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<User> userList = sqlSession.selectList("UserMapper.findAll");
        return userList;
    }
}
           

測試如下:

public static void main(String[] args) throws IOException {
        UserMapper userMapper = new UserMapperImpl();
        List<User> users = userMapper.findAll();
        System.out.println(users);
}
           

這種方式是比較繁瑣的一種方式不是很常用,當我們使用插件MyBatisX時,寫好對應的dao方法後會出現一隻鳥的标志,通過該标志就可以找到我們在配置檔案中寫的對應的配置了。是以我們可以通過插件的跳轉顯示來判别我們寫的是否是規範的。

接口代理方式

使用這種方式隻需要去編寫Mapper接口,由架構根據接口定義建立接口的動态代理對象,代理對象的方法體就是使用傳統方式實作接口的方法體。但是這種方式需要遵循規範,如:Mapper.xml檔案中的namespace和mapper接口的全限定名相同、Mapper接口方法名和Mapper.xml中定義的每個statement的id相同、Mapper接口方法的輸入參數類型和Mapper.xml中定義的每個sql的parameterType的類型相同、Mapper接口方法的輸出參數類型和Mapper.xml中定義的每個sql的resultType的類型相同。這種方式也是目前的主流開發方式,上述所說的使用插件進行輔助開發,在這裡得以運用。具體操作如下:

映射檔案:

<?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.jclight.mapper.UserMapper">
    <select id="findAll" resultType="user">
        select * from user;
    </select>
    <select id="findById" resultType="user" parameterType="int">
        select * from user where id=#{id};
    </select>
</mapper>
           

接口:

public interface UserMapper {
    /**
     * 在User表中查詢全部資料
     *
     * @return User對象組成的集合
     * @throws IOException 将IO異常抛出
     */
    List<User> findAll() throws IOException;

    /**
     * 根據id進行查詢
     *
     * @param id 使用者id
     * @return User對象
     */
    User findById(int id);
}
           

測試:

@Test
public void findByIdTest() throws IOException {
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    com.jclight.mapper.UserMapper mapper = sqlSession.getMapper(com.jclight.mapper.UserMapper.class);
    User user = mapper.findById(1);
    System.out.println(user);
}
           

如此便會執行成功,這樣的方式不需要我們去寫實作就可以執行資料庫操作。之後還可以使用Spring架構內建MyBatis将代碼簡化等。插件的跳轉提示如下圖:

持久層架構學習——MyBatis(一)
持久層架構學習——MyBatis(一)

此時就可以進行相應的跳轉了,後期開發中我們的方法過多不易快速找到對應的配置,是以使用該插件可以很好的幫助我們進行定位。