1. 程式人生 > >開源頂級持久層框架——mybatis(ibatis)——day01

開源頂級持久層框架——mybatis(ibatis)——day01

mybatis-day01
    1.對原生態jdbc程式中的問題總結
        1.1環境
            java環境:jdk
            eclipse:indigo
            mysql:xx

            mybatis x.x.x

        1.2jdbc程式
            使用jdbc查詢mysql資料中使用者表的記錄

        1.4問題總結
            1.資料庫連線,使用時就建立,不適用就立即釋放,對資料庫進行頻繁的連線開啟和關閉,造成了資料庫資源浪費,影響了資料庫效能。
            解決方案:使用資料庫連線池管理資料庫連線。

            2.將sql語句硬編碼到java程式碼中,如果sql語句修改,需要重新編譯java程式碼,不利於系統維護不需要對java程式碼進行重新編譯。

            3.想preparedStatement中設定引數,對佔位符位置和設定引數值,硬編碼在java程式碼中,不利於系統維護。
            解決方案:將sql語句及佔位符和引數全部配置在xml中。

            4.從resultSet中遍歷結果集資料時,存在硬編碼,在獲取表的欄位進行硬編碼,不利於系統維護。
            解決方案:將查詢的結果集,自動對映成java物件

    2.mybatis框架
        2.1mybatis是什麼?
            mybatis是一個持久層的框架,是apache下的頂級專案。
            mybatis託管到Google code,再後來託管到github下(https://github.com/mybatis/mybatis-3/releases)
            mybatis讓程式將主要精力防砸sql上,通過mybatis提供的對映方式,自由靈活生成(半自動化,大部分需要程式設計師編寫sql)滿足需要sql語句。
            mybatis可以將向preparedStatement中的輸入引數自動進行輸入對映,將查詢結果集靈活對映成java物件。(輸出對映)
        
        2.2mybatis框架圖示

    3.入門程式
        3.1需求
            根據使用者id(主鍵)查詢使用者資訊
            更加使用者名稱稱模糊查詢使用者資訊
            新增使用者
            刪除使用者
            更新使用者

        3.2環境
            java環境:jdk
            eclipse:indigo
            mysql:x.x

            mybatis執行環境(jar包)
            從http://github.com/mybatis/mybatis-3/releases下載,

            在下載的檔案中:
                lib:依賴包
                mybatis-x.x.x.jar:核心包
                mybatis-x.x.x.pdf:操作指南

                加入mysql的驅動包

        3.3log4j.properties
            # Global logging configuration
            #\u5728\u5f00\u53d1\u73af\u5883\u4e0b\u65e5\u5fd7\u7ea7\u522b\u8981\u8bbe\u7f6e\u6210DEBUG\uff0c\u751f\u4ea7\u73af\u5883\u8bbe\u7f6e\u6210info\u6216error
            log4j.rootLogger=DEBUG, stdout
            # Console output...
            log4j.appender.stdout=org.apache.log4j.ConsoleAppender
            log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
            log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

        3.4工程結構

        3.5SqlMapConfig.xml
            配置mybatis的執行環境,資料來源、事務等。
            <?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>
                <!-- 和spring整合後 environments配置將廢除 -->
                <environments default="development">
                    <environment id="development">
                        <!-- 使用jdbc事務管理,事務控制由mybatis -->
                        <transactionManager type="JDBC" />
                        <!-- 資料庫連線池,由mybatis管理 -->
                        <dataSource type="POOLED">
                            <property name="driver" value="com.mysql.jdbc.Driver" />
                            <property name="url"
                                value="jdbc:mysql://localhost:3306/sql_mybatis?characterEncoding=utf-8" />
                            <property name="username" value="root" />
                            <property name="password" value="123456" />
                        </dataSource>
                    </environment>
                </environments>

                <!-- 載入對映檔案 -->
                <mappers>
                    <mapper resource="sqlmap/User.xml" />
                </mappers>

            </configuration>

        3.6根據使用者id(主鍵)查詢使用者資訊
            3.6.1建立po類
            public class User {
                // 屬性名和資料庫表的欄位對應
                private int id;
                private String username;// 使用者姓名
                private String sex;// 性別
                private Date birthday;// 生日
                private String address;// 地址

            3.6.2對映檔案
                對映檔案命名:
                    User.xml(原始ibatis命名),mapper代理開發對映檔名稱叫XXXMapper.xml,比如:UserMapper.xml、ItemsMapper.xml在對映檔案中配置sql語句。

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

                        <!-- namespace名稱空間,作用就是對sql進行分類化管理,理解sql隔離
                         注意:使用mapper代理方法開發,namespace有特殊重要的作用 
                         -->
                        <mapper namespace="test">
                            <!-- 在對映檔案中配置很多sql語句 -->
                            <!-- 需求:通過di查詢使用者表的記錄 -->
                            <!-- 通過select執行資料庫查詢
                            id:標識對映檔案中的sql,稱為statement的id
                            將sql語句封裝到mappedStatement物件中,所以將id稱為statement的id
                            
                            parameterType:指定輸入引數的型別,這裡指定int型
                            
                            #{}表示一個佔位符號
                            
                            #{id}:其中的id表示接收輸入的引數,引數名稱就是id,如果輸入引數是簡單型別,#{}中的引數可以任意,可以value或其他名稱
                            
                            resultType:指定輸出結果所對映的java物件型別,select指定resultType表示將單條記錄對映成java物件。
                             -->
                            <select id="findUserById" parameterType="int" resultType="com.changemax.mybatis.po.User">
                                SELECT * FROM user WHERE id = #{id}
                            </select>
                            
                            
                            <!-- 根據使用者名稱稱模糊查詢使用者資訊,可能返回多條
                                resultType:指定就是單條記錄所對映的java物件型別 
                                ${}:表示拼接sql串,將接受到的引數的內容不加任何修飾拼接在sql中。
                                使用${}容易出現sql注入
                                ${value}:表示接收輸入引數的內容,如果傳入型別是簡單型別,${}中只能使用value-->
                            <select id="findUserByLikeName" parameterType="java.lang.String" resultType="com.changemax.mybatis.po.User">
                                SELECT * FROM user WHERE username LIKE  '%${value}%'
                            </select>
                        </mapper>

            3.6.3在SqlMapConfig.xml載入對映檔案
                在sqlMapConfig.xml中載入User.xml
                <!-- 載入對映檔案 -->
                <mappers>
                    <mapper resource="sqlmap/User.xml"/>
                </mappers>

            3.6.4程式編寫
                // 根據id查詢使用者資訊,得到一條記錄結果
                @Test
                public void findUserByIdTest() throws IOException {

                    SqlSession session = null;

                    try {

                        // mybatis配置檔案
                        String resource = "SqlMapConfig.xml";

                        // 得到配置檔案流
                        InputStream inputStream = Resources.getResourceAsStream(resource);

                        // 建立會話工廠,要想build中傳入mybatis配置資訊
                        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

                        // 通過工廠得到SqlSession
                        session = factory.openSession();

                        // 通過SqlSession操作資料庫
                        // 第一個引數:對映檔案中statement種的id,等於=namespace+"."+statement的id
                        // 第二個引數:指定和對映檔案中所匹配的paramenterType型別的引數
                        // session.selectOne結果是與對映檔案中所匹配的resultType型別的物件
                        User user = session.selectOne("test.findUserById", 1);

                        System.out.println(user);
                    } catch (Exception e) {
                        // TODO: handle exception
                    } finally {
                        if (session != null) {
                            // 釋放資源
                            session.close();
                        }
                    }

                }


        3.7根據使用者名稱稱模糊查詢使用者資訊
            3.7.1對映檔案
                使用User.xml,新增根據使用者名稱模糊查詢使用者資訊的sql語句。

                    <!-- 根據使用者名稱稱模糊查詢使用者資訊,可能返回多條
                        resultType:指定就是單條記錄所對映的java物件型別 
                        ${}:表示拼接sql串,將接受到的引數的內容不加任何修飾拼接在sql中。
                        使用${}容易出現sql注入
                        ${value}:表示接收輸入引數的內容,如果傳入型別是簡單型別,${}中只能使用value-->
                    <select id="findUserByLikeName" parameterType="java.lang.String" resultType="com.changemax.mybatis.po.User">
                        SELECT * FROM user WHERE username LIKE  '%${value}%'
                    </select>

            3.7.2程式程式碼
                // 根據使用者名稱稱模糊查詢使用者
                @Test
                public void findUserByLikeNameTest() throws IOException {

                    SqlSession session = null;
                    try {

                        // mybatis配置檔案
                        String resource = "SqlMapConfig.xml";

                        // 得到配置檔案流
                        InputStream inputStream = Resources.getResourceAsStream(resource);

                        // 建立會話工廠,要想build中傳入mybatis配置資訊
                        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

                        // 通過工廠得到SqlSession
                        session = factory.openSession();

                        // list中的user和對映檔案中result中resultType所指定的型別一致
                        List<User> userList = session.selectList("test.findUserByLikeName", "小明");

                        for (User user : userList) {
                            System.out.println(user);
                        }

                    } catch (Exception e) {
                        // TODO: handle exception
                    } finally {
                        if (session != null) {
                            // 釋放資源
                            session.close();
                        }
                    }

                }

        3.8新增使用者
            3.8.1對映檔案
                在User.xml中配置喲新增使用者的Statement
                    <!-- 新增使用者 
                    parameterType:指定輸入引數型別是pojo(包括使用者資訊)
                    #{}中指定pojo的屬性名,接收到pojo物件的屬性值,mybatis通過OGNL獲取物件的屬性值
                    -->
                    <insert id="insertUser" parameterType="com.changemax.mybatis.po.User">
                        INSERT INTO user(id, username, birthday, sex, address) value(#{id}, #{username}, #{birthday}, #{sex}, #{address})
                    </insert>

            3.8.2程式程式碼
                // 插入使用者
                @Test
                public void insertUserTest() throws IOException {

                    SqlSession session = null;
                    try {

                        // mybatis配置檔案
                        String resource = "SqlMapConfig.xml";

                        // 得到配置檔案流
                        InputStream inputStream = Resources.getResourceAsStream(resource);

                        // 建立會話工廠,要想build中傳入mybatis配置資訊
                        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

                        // 通過工廠得到SqlSession
                        session = factory.openSession();

                        // 插入使用者物件
                        User user = new User();
                        user.setUsername("麥兜");
                        user.setBirthday(new Date());
                        user.setAddress("江西九江");
                        user.setSex("男");

                        session.insert("test.insertUser", user);
                        System.out.println(user.getId()+"---------------------------");

                        session.commit();

                    } catch (Exception e) {
                        session.rollback();
                    } finally {
                        if (session != null) {
                            // 釋放資源
                            session.close();
                        }
                    }

                }

            3.8.3自增主鍵返回
                mysql自增主鍵,執行insert提交之前自動生成一個自增主鍵。
                通過mysql函式獲取到剛剛插入記錄1自增主鍵:
                    LAST_INSERT_ID()

                是insert之後呼叫此函式。

                修改insert定義:

                    <!-- 新增使用者 
                    parameterType:指定輸入引數型別是pojo(包括使用者資訊)
                    #{}中指定pojo的屬性名,接收到pojo物件的屬性值,mybatis通過OGNL獲取物件的屬性值
                    -->
                    <insert id="insertUser" parameterType="com.changemax.mybatis.po.User">
                        
                        <!-- 將插入資料的主鍵返回,返回到user物件中
                        SELECT LAST_INSERT_ID():得到剛insert景區記錄的主鍵值,只適用於自增主鍵
                        
                        keyProperty:將查詢到主鍵值設定到parameterType指定的物件的那個屬性
                        order:SELECT LAST_INSERT_ID()執行順序,相對於insert語句來說它的執行順序
                         -->
                        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
                            SELECT LAST_INSERT_ID()
                        </selectKey>
                    
                        INSERT INTO user(id, username, birthday, sex, address) value(#{id}, #{username}, #{birthday}, #{sex}, #{address})
                    </insert>    

            3.8.4非自增抓緊返回(使用uuid())
                使用mysql的uuid()函式生成主鍵,需要修改表表中id欄位型別為string,長度設定為35位。

                執行思路:
                    先通過uuid()查詢到主鍵,將主鍵輸入到sql語句中。

                    執行uuid()語句順序相對於insert語句之前執行。
                        <!-- 適用mysql的uuid()生成主鍵 
                        執行的過程:
                            首先通過uuid()得到主鍵,將主鍵設定到user物件的id屬性中
                            其次在insert執行時,從user物件中取出id屬性值
                        -->
                        <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
                            SELECT UUID()
                        </selectKey>
                        INSERT INTO user(id, username, birthday, sex, address) value(#{id}, #{username}, #{birthday}, #{sex}, #{address})
            
                通過oracle的序列生成主鍵:

                    <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
                        SELECT 序列名.nextval()
                    </selectKey>
                    insert into user(id,username,birthday,sex,address) value(#{id},#{username},#{birthday},#{sex},#{address})

        3.9刪除使用者
            3.9.1對映檔案
                <delete id="deleteUser" parameterType="java.lang.Integer">
                    DELETE FROM user WHERE id = #{id}
                </delete>

            3.9.2程式碼:
                // 刪除使用者
                @Test
                public void deleteUserTest() throws IOException {

                    SqlSession session = null;
                    try {

                        // mybatis配置檔案
                        String resource = "SqlMapConfig.xml";

                        // 得到配置檔案流
                        InputStream inputStream = Resources.getResourceAsStream(resource);

                        // 建立會話工廠,要想build中傳入mybatis配置資訊
                        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

                        // 通過工廠得到SqlSession
                        session = factory.openSession();

                        session.delete("test.deleteUser", 27);

                        session.commit();

                    } catch (Exception e) {
                        session.rollback();
                    } finally {
                        if (session != null) {
                            // 釋放資源
                            session.close();
                        }
                    }

                }

        3.10更新使用者
            3.10.1對映檔案
                <update id="updateUser" parameterType="com.changemax.mybatis.po.User">
                    UPDATE user SET username = #{username}, birthday = #{birthday}, sex = #{sex}, address = #{address} WHERE id = #{id}
                </update>

            3.10.2程式碼
                // 更新使用者
                @Test
                public void updateUserTest() throws IOException {

                    SqlSession session = null;
                    try {

                        // mybatis配置檔案
                        String resource = "SqlMapConfig.xml";

                        // 得到配置檔案流
                        InputStream inputStream = Resources.getResourceAsStream(resource);

                        // 建立會話工廠,要想build中傳入mybatis配置資訊
                        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

                        // 通過工廠得到SqlSession
                        session = factory.openSession();

                        User user = session.selectOne("test.findUserById", 29);

                        user.setUsername("王際");

                        session.update("test.updateUser", user);

                        session.commit();

                    } catch (Exception e) {
                        e.printStackTrace();
                        session.rollback();
                    } finally {
                        if (session != null) {
                            // 釋放資源
                            session.close();
                        }
                    }

                }

        3.11總結:
            3.11.1parameterType
                在對映檔案中通過parameterType指定輸入引數的型別。

            3.11.2resultType
                在對映檔案中通過resultType指定輸出結果的型別。

            3.11.3#{}和${}
                #{}表示一個佔位符合,#{}接收輸入引數,型別可以是簡單型別,pojo、hashmap。
                如果接收簡單型別,#{}中可以寫成value或其他名稱。
                #{}接收pojo物件值,通過OGNL讀取物件中的屬性值,通過屬性.屬性.屬性...的方式獲取物件屬性值。

                ${}表示一個拼接符號,會引用sql注入,所以不建議使用${}。
                ${}接收輸入引數,型別可以是簡單型別,pojo、hashmap。
                如果接收簡單型別,${}只能寫成value。
                ${}接收pojo物件值,通過OGNL讀取物件中的屬性值,通過屬性.屬性.屬性...的方式獲取物件屬性值。


            3.11.4selectOne和selectList
                selectOne表示查詢出一條記錄進行對映。如果使用selectOne可以實現使用selectList也可以實現(list中只用一個物件)。

                selectList表示查詢出一個列表(多條記錄)進行對映。如果使用selectList查詢多條記錄,不能使用selectOne。

                如果使用selectOne會報錯:
                    org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4


        3.12mybatis和hibernate本質區別和應用場景
            hibernate:是一個標準的ORM框架(物件關係對映)。入門門檻較高,不需要程式寫sql,sql語句自動生成的。
            對sql語句進行優化、修改比較困難的。
                應用場景:
                    適用與需求變化不多的中小型專案,比如:後臺管理系統,erp,orm,oa..

            mybatis:專注於sql本省,需要程式設計師自己編寫sql語句,sql修改,優化比較方便。mybatis是一個不完全的ORM框架,雖然程式設計師自動寫slq,mybatis也可以實現對映(輸入對映、輸出對映)。
                應用場景:
                    適用於需求變換較多的專案,比如:網際網路專案。

            企業進行技術選型,以低成本    高回報作為技術選型的原則,跟據專案組的技術力量進行選擇。


    4.mybatis開發dao的方法
        4.1SqlSession使用方法
            4.1.1SqlSessionFactoryBuilder
                通過SqlSessionFactoryBuilder建立會話工廠SqlSessionFactory,將SqlSessionFactoryBuilder當成一個工具類使用即可,不需要使用單例管理SqlSessionFactoryBuilder。
                在需要建立SqlSessionFactory時,只需要new一次SqlSessionFactoryBuilder即可。

            4.1.2SqlSessionFactory
                通過SqlSessionFactory建立SqlSession,使用單例模式管理sqlSessionFactory(工廠一旦建立,使用一個例項)。

            4.1.3SqlSession
                SqlSession是一個面向使用者(程式設計師)的介面
                SqlSession中提供了很多操作資料庫的方法:如:selectOne(返回單個物件)、selectList(範湖單個或多個物件)。

                SqlSession是執行緒不安全的,在SqlSession實現類中除了有介面中的方法(操作資料庫的方法)還有資料域屬性。

                SqlSession最佳應用場合在方法體內,定義成區域性變數使用

        4.2原始dao開發方法(程式設計師需要寫dao介面和dao實現類)
            4.2.1思路
                程式設計師需要寫dao介面和dao實現類。
                需要向doa實現類中注入SqlSessionFactory,在方法體內通過SqlSessionFactory建立SqlSession

            4.2.2dao介面
                public interface UserDao {
                    // 根據id查詢使用者資訊
                    public User findUserById(int id) throws Exception;

                    // 根據使用者名稱查詢使用者資訊
                    public User findUserByName(String username) throws Exception;

                    // 新增使用者資訊
                    public void insertUser(User user) throws Exception;

                    // 刪除使用者資訊
                    public void deleteUser(int id) throws Exception;
                }

            4.2.3dao介面實現類
                public class UserDaoImpl implements UserDao {
                    private SqlSessionFactory sqlSessionFactory;

                    // 需要向dao實現類中注入SqlSessionFactory
                    public UserDaoImpl(SqlSessionFactory factory) {
                        this.sqlSessionFactory = factory;
                    }

                    @Override
                    public User findUserById(int id) throws Exception {
                        SqlSession sqlSession = null;
                        try {
                            sqlSession = sqlSessionFactory.openSession();
                            List<User> userList = sqlSession.selectList("test.findUserById", id);
                            if (userList.isEmpty() && userList.size() > 0) {
                                return userList.get(0);
                            }
                            sqlSession.commit();
                        } catch (Exception e) {
                            e.printStackTrace();
                            sqlSession.rollback();
                        } finally {
                            if (sqlSession != null) {
                                sqlSession.close();
                            }
                        }
                        return null;
                    }

                    @Override
                    public User findUserByName(String username) throws Exception {
                        SqlSession sqlSession = null;
                        try {
                            sqlSession = sqlSessionFactory.openSession();
                            List<User> userList = sqlSession.selectList("test.findUserByName", username);
                            if (userList.isEmpty() && userList.size() > 0) {
                                return userList.get(0);
                            }
                            sqlSession.commit();
                        } catch (Exception e) {
                            e.printStackTrace();
                            sqlSession.rollback();
                        } finally {
                            if (sqlSession != null) {
                                sqlSession.close();
                            }
                        }
                        return null;
                    }

                    @Override
                    public void insertUser(User user) throws Exception {
                        SqlSession sqlSession = null;
                        try {
                            sqlSession = sqlSessionFactory.openSession();
                            sqlSession.insert("test.insertUser", user);
                            sqlSession.commit();
                        } catch (Exception e) {
                            e.printStackTrace();
                            sqlSession.rollback();
                        } finally {