1. 程式人生 > >mybatis——開發dao層

mybatis——開發dao層

文章目錄

1 SqlSession生命週期

  1. sqlsession:方法級別
  2. sqlsessionFactory:全域性範圍(應用級別)
  3. sqlsessionFactoryBuilder:方法級別

1.1 SqlSessionFactoryBuilder

SqlSessionFactoryBuilder用於建立SqlSessionFacoty,SqlSessionFacoty一旦建立完成就不需要SqlSessionFactoryBuilder了,因為SqlSession是通過SqlSessionFactory生產,所以可以將SqlSessionFactoryBuilder當成一個工具類使用,最佳使用範圍是方法範圍(即方法體內區域性變數)。

1.2 SqlSessionFactory

SqlSessionFactory是一個介面,介面中定義了openSession的不同過載方法,SqlSessionFactory的最佳使用範圍是整個應用執行期間,一旦建立後可以重複使用,通常以單例模式管理SqlSessionFactory。

1.3 SqlSession

SqlSession中封裝了對資料庫的操作,如查詢、插入、更新、刪除等。通過SqlSessionFactory建立SqlSession,而SqlSessionFactory是通過SqlSessionFactoryBuilder進行建立的。
SqlSession是一個面向程式設計師的介面,SqlSession中定義了資料庫操作方法,所以SqlSession作用是操作資料庫,並且SqlSession物件要儲存資料庫連線、事務和一級快取結構等。
每個執行緒都應該有它自己的SqlSession例項。SqlSession的例項不能共享使用,它是執行緒不安全的(多執行緒訪問系統,當多執行緒同時使用一個SqlSession物件時會造成資料衝突問題)。由於SqlSession物件是執行緒不安全的,因此它的最佳使用範圍是請求或方法範圍(也可說為SqlSession的最佳使用場合是在方法體內作為區域性變數來使用),絕對不能將SqlSession例項的引用放在一個類的靜態欄位或例項欄位中。
開啟一個SqlSession,使用完畢就要關閉它。通常把這個關閉操作放到finally塊中以確保每次都能執行關閉。如下:

SqlSession session = sqlSessionFactory.openSession();
try {
   
} finally {
    session.close();
}

2 原始Dao開發方式

原始Dao開發方法需要程式設計師自己編寫Dao介面和Dao實現類。

2.1 UserMapper.xml和SqlMapConfig.xml

UserMapper.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="user">
    <select id="findUserById" parameterType="int" resultType="User">
        SELECT * from user WHERE id=#{id}
    </select>

    <insert id="addUser" parameterType="User">
        <!-- selectKey將主鍵返回,需要再返回 -->
        <selectKey keyProperty="id" order="AFTER" resultType="int">
            select LAST_INSERT_ID()
        </selectKey>
        INSERT INTO user(username,password,email,phone,address)
        VALUES (#{username},#{password},#{email},#{phone},#{address})
    </insert>
    
    <select id="getUserByName" parameterType="String" resultType="user">
        SELECT * FROM user where username=#{username}
    </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>
    <properties resource="db.properties"></properties>
    <typeAliases>
        <typeAlias type="pojo.User" alias="user"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}" />
                <property name="url" value="${db.url}" />
                <property name="username" value="${db.username}" />
                <property name="password" value="${db.password}" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml" />
    </mappers>
</configuration>

2.2 UserDao和UserDaoImp

public interface UserDao {
    User findUserbyId(int id);
    void addUser(User user);
    List<User> getUserByName(String username);
}

UserDaoImpl.java:

public class UserDaoImp implements UserDao {
    private SqlSessionFactory sqlSessionFactory;
    public UserDaoImp(SqlSessionFactory sqlSessionFactory){
        this.sqlSessionFactory =sqlSessionFactory;
    }
    public User findUserbyId(int id) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = sqlSession.selectOne("findUserById",id);
        return user;
    }
    public void addUser(User user) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.insert("addUser", user);
        sqlSession.commit();
        if (sqlSession!=null)
            sqlSession.close();
    }

    public List<User> getUserByName(String username) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<User> users =sqlSession.selectList("getUserByName",username);
        return users;
    }
}

2.2 測試

public class UserDaoTest {
    private SqlSessionFactory sqlSessionFactory = null; // 工廠物件一般在我們的系統中是單例的
    @Before
    public void init() throws IOException {
        // 第一步,建立SqlSessionFactoryBuilder物件
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 第二步,載入配置檔案
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 第三步,建立SqlSessionFactory物件
        sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
    }
    @Test
    public void testFindUserById(){
        UserDao userDao = new UserDaoImp(sqlSessionFactory);
        User u =userDao.findUserbyId(1);
        System.out.println(u);
    }

    @Test
    public void testAddUser(){
        UserDao userDao = new UserDaoImp(sqlSessionFactory);
        User user = new User("小xiao小", "111", "[email protected]", "123", "sasas");
        userDao.addUser(user);
    }

    @Test
    public void testGetUserByName(){
        UserDao userDao = new UserDaoImp(sqlSessionFactory);
        List<User> users =userDao.getUserByName("小小");
        for (User u:users)
            System.out.println(u);
    }
}

2.3 原始Dao開發方式所帶來的問題

從以上UserDaoImpl類的程式碼可看出原始Dao開發存在以下問題:

  1. dao介面實現類方法中存在大量的重複程式碼,這些重複的程式碼就是模板程式碼。
    模板程式碼為:
    先建立sqlsession
    再呼叫sqlsession的方法
    再提交sqlsession
    再關閉sqlsession
    設想能否將這些程式碼提取出來,這可大大減輕程式設計師的工作量。
  2. 呼叫sqlSession的資料庫操作方法需要指定statement的id,這裡存在硬編碼,不得於開發維護。
  3. 呼叫sqlsession方法時傳入的變數,由於sqlsession方法使用泛型,即使變數型別傳入錯誤,在編譯階段也不報錯,不利於程式設計師開發。

3 Mapper動態代理開發方式

設計模式——動態代理
Mapper介面開發方法只需要程式設計師編寫Mapper介面,由Mybatis框架根據介面定義建立介面的動態代理物件,代理物件的方法體同上邊Dao介面實現類方法。

3.1 Mapper介面開發需要遵循以下規範:

Mapper.xml檔案中的namespace與mapper介面的類路徑相同,即namespace必須是介面的全限定名。
Mapper介面方法名和Mapper.xml中定義的每個statement的id相同。
Mapper介面方法的輸入引數型別和mapper.xml中定義的每個sql的parameterType的型別相同。
Mapper介面方法的輸出引數型別和mapper.xml中定義的每個sql的resultType的型別相同。

3.2 編寫Mapper.xml(對映檔案)

我們可在config原始碼目錄下新建一個mapper的普通資料夾,該資料夾專門用於存放對映檔案。然後在該資料夾下建立一個名為mapper.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="mapper.UserMapper">
    <select id="findUserById" parameterType="int" resultType="User">
        SELECT * from user WHERE id=#{id}
    </select>

    <insert id="addUser" parameterType="User">
        <!-- selectKey將主鍵返回,需要再返回 -->
        <selectKey keyProperty="id" order="AFTER" resultType="int">
            select LAST_INSERT_ID()
        </selectKey>
        INSERT INTO user(username,password,email,phone,address)
        VALUES (#{username},#{password},#{email},#{phone},#{address})
    </insert>

    <select id="getUserByName" parameterType="String" resultType="user">
        SELECT * FROM user where username=#{username}
    </select>
</mapper>

3.3 編寫Mapper介面

建立一個Mapper介面——UserMapper.java:

public interface UserMapper {
    User findUserById(Integer id);
    void addUser(User user);
    List<User> getUserByName(String username);
}

3.4 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>
    <properties resource="db.properties"></properties>
    <typeAliases>
       <!-- <typeAlias type="pojo.User" alias="user"/>-->
        <package name="pojo"/>
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}" />
                <property name="url" value="${db.url}" />
                <property name="username" value="${db.username}" />
                <property name="password" value="${db.password}" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
       <!--<mapper resource="mapper/UserMapper.xml" />-->
        <package name="mapper"/>
    </mappers>
</configuration>

3.5 UserMapperTest

public class UserMapperTest {
    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void creatSqlSessionFactory() throws IOException {
        String resource = "SqlMapConfig.xml";
        InputStream in = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    }

    @Test
    public void testFindUserById(){
        // 和Spring整合後就省略了
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 獲得代理物件(到時候就只需要通過Spring容器拿到UserMapper介面的代理物件就可以了)
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User u =userMapper.findUserById(1);
        System.out.println(u);
    }

    @Test
    public void testAddUser(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = new User("小xiao小", "111", "[email protected]", "123", "sasas");
        userMapper.addUser(user);
    }

    @Test
    public void testGetUserByName(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> users =userMapper.getUserByName("小小");
        for (User u:users)
            System.out.println(u);
    }
}

4 小結

4.1 selectOne和selectList

動態代理物件呼叫sqlSession.selectOne()和sqlSession.selectList()是根據mapper介面方法的返回值決定,如果返回list則呼叫selectList方法,如果返回單個物件則呼叫selectOne方法。

4.2 namespace

mybatis官方推薦使用mapper代理方法開發mapper介面,程式設計師不用編寫mapper介面實現類,使用mapper代理方法時,輸入引數可以使用pojo包裝物件或map物件,保證dao的通用性。

4.3 SqlMapConfig.xml配置檔案

SqlMapConfig.xml檔案中配置的內容和順序如下:

properties(屬性)
settings(全域性配置引數)
typeAliases(類型別名)
typeHandlers(型別處理器)
objectFactory(物件工廠)
plugins(外掛)
environments(環境集合屬性物件)
environment(環境子屬性物件)
transactionManager(事務管理)
dataSource(資料來源)
mappers(對映器)
properties(屬性)

如果不按順序配置,xml會報錯

4.3.1 資料庫配置

資料配置屬性方法一

<properties>
    <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
    <property name="jdbc.url" value="jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8"/>
</properties>

資料配置屬性方法二

db.driver=com.mysql.jdbc.Driver
db.url = jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8
db.username = root
db.password=root
<properties resource="db.properties"></properties>
<dataSource type="POOLED">
	<property name="driver" value="${db.driver}" />
	<property name="url" value="${db.url}" />
	<property name="username" value="${db.username}" />
	<property name="password" value="${db.password}" />
</dataSource>

載入properties順序

  1. 先讀取在properties元素體內定義的屬性
  2. 然後會讀取properties元素中resource或url載入的屬性,它會覆蓋已讀取的同名屬性

4.3.2 typeAliases(類型別名)

1. mybatis支援別名

別名 對映的型別
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
map Map

2. 自定義別名
2.1配置單個別名

<typeAliases>
    <!-- 單個別名定義  別名不區分大小寫 -->
    <typeAlias type="pojo.User" alias="user"/>
</typeAliases>

2.2配置掃描包的別名

<!-- 配置pojo的別名 -->
<typeAliases>
    <!-- 批量別名定義,掃描包的形式建立別名,別名就是類名,且不區分大小寫 -->
    <package name="pojo"/>
</typeAliases>

4.3.3 SqlMapConfig.xml檔案載入mapper.xml檔案

<mappers>
        <!-- 資源路徑載入 -->
       <mapper resource="mapper/UserMapper.xml" />
       <!-- 類路徑載入 -->
        <!--注意:此種方法要求mapper介面名稱和mapper對映檔名稱相同,且放在同一個目錄中。-->
        <mapper class="mapper/UserMapper.xml"/>
        <!--注意:此種方法要求mapper介面名稱和mapper對映檔名稱相同,且放在同一個目錄中。-->
        <package name="mapper"/>
</mappers>