Mybatis學習筆記(一)——基本的CRUD操做

MyBatis是什麼

mybatis是託管在github上的ORM框架,讓程序員將主要精力放在SQL上,經過mybatis提供映射方式,自由靈活(SQL的可定製性較高,半自動化)生成知足需求的SQL語句。mybatis能夠將向 preparedStatement中的輸入參數自動進行輸入映射,將查詢結果集靈活映射成java對象。(輸出映射java

在進行項目的編碼以前首先初始化數據庫,項目所須要的sql腳本mysql

原生jdbc中的問題總結

觀察如下的代碼:git

@Test
public void testJDBC(){
    final String DB_DRIVER = "org.gjt.mm.mysql.Driver";
    final String DB_URL = "jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf-8";
    final String DB_USER = "root";
    final String DB_PASSWORD = "mysqladmin";
    
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    
    try {
        Class.forName(DB_DRIVER);
        conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
        String sql = "SELECT * FROM user WHERE username = ?";
        pstmt = conn.prepareStatement(sql);
        pstmt.setString(1, "王五");
        rs = pstmt.executeQuery();
        while (rs.next()) {
            System.out.println("id:" + rs.getInt("id") + ",username:" + rs.getString("username"));
        }
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    } finally {
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if( pstmt != null){
            try {
                pstmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

以上的程序中存在如下的問題:程序員

  • 數據庫使用時打開鏈接,不使用的時候當即釋放鏈接。對數據庫進行了頻繁的打開和關閉,影響性能(設想:可使用DBPool)。github

  • 將SQL語句硬編碼到java代碼中,修改SQL語句須要從新編譯java代碼(設想:使用配置文件配置SQL)。sql

  • 在向PreparedStatement中設置參數的時候參數的位置和參數值硬編碼在代碼中(設想:配置文件)。shell

  • 從結果集中遍歷數據的時候存在硬編碼。(設想:把結果集映射成javabean)數據庫

MyBatis原理

  • SqlMapConfig.xml:MyBatis全局配置文件(數據源、事務等mybatis運行環境),不是固定名稱;apache

  • mapper.xml:映射關係(配置SQL語句);緩存

  • SqlSessionFactory:建立會話(SqlSession);

  • SqlSession:操做DB(CRUD),面向用戶的接口;

  • Excutor:執行器(SqlSession內部經過執行器操做DB),接口(有2個實現類,基本執行器和緩存執行器);

  • mappedstatement:底層封裝對象(sql語句、輸入參數、結果類型)。

mybatis原理圖

入門程序

需求:

  • 根據id(主鍵)查詢用戶信息

  • 根據用戶名模糊查詢用戶信息

  • 添加用戶

  • 更新用戶

  • 刪除用戶

在新建一個源代碼目錄config,在config目錄下使用如下的log4j屬性文件(能夠從mybatis示例程序中拷貝):

編碼前的準備

log4j

# 開發環境中日誌的級別使用DEBUG,生產環境中日誌級別爲ERROR
### Global logging configuration
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

工程目錄以下所示

mybatis工程目錄

在SqlMapConfig.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 default="development">
        <environment id="development">
            <!-- 使用JDBC事務進行管理,事務的控制由mybatis管理 -->
            <transactionManager type="JDBC">
                <property name="" value="" />
            </transactionManager>
            <!-- 數據庫鏈接池,由mybatis管理 -->
            <dataSource type="UNPOOLED">
                <property name="driver" value="org.gjt.mm.mysql.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8" />
                <property name="username" value="root" />
                <property name="password" value="mysqladmin" />
            </dataSource>
        </environment>
    </environments>
</configuration>

根據id查詢用戶信息

1.新建一個pojo名稱爲User,其字段以下:

private int id;
private String username;
private String sex;
private Date birthday;
private String address;

2.在映射文件(User.xml,這是原始的ibatis映射方式,若是採用mapper代理則名稱應該是UserMapper.xml)中配置sql語句。

config/sqlmap目錄下新建一個User.xml(輸入的參數類型和輸出的結果集映射的javabean)內容以下:

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

<!-- 命名空間用來對SQL進行分類管理(SQL隔離)
    注意:
        若是採用Mapper代理模式開發,該命名空間具備重要意義
 -->    
<mapper namespace="test">

<!-- 在映射文件中配置SQL語句 -->

<!-- 此處的id用來標識映射文件中的SQL,爲Statement的id,將SQL語句封裝到mappedstatement對象中-->
<!-- 經過id查詢用戶 

     parameterType   指定輸入參數的類型(id是int)
     #{}             就是jdbc中的佔位符
     #{id}              其中的id是參數名,若是輸入的參數是簡單類型,#{}中的參數能夠任意
     resultType          指定SQL語句結果集所映射的JavaBean
-->
<select id="findUserById" parameterType="int" resultType="org.gpf.po.User">
    SELECT * FROM user WHERE id = #{id};
</select>

</mapper>

3.在SqlMapConfig.xml中加載User.xml.(在User.xml的configuration中加入如下內容)

<mappers>
    <mapper resource="sqlmap/User.xml"/>
</mappers>

4.編寫測試類:

/**
 * 根據id查詢用戶信息
 */
@Test
public void testFindUserById() throws IOException{
    
    // mybatis配置
    String resource = "SqlMapConfig.xml";
    
    // 獲得配置文件的流
    InputStream is = Resources.getResourceAsStream(resource);
    
    // 建立會話工廠(傳入配置信息)
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    
    // 經過會話工廠獲得Sqlsession
    SqlSession session = sqlSessionFactory.openSession();
    
    // 查詢
    // 參數一:命名空間下的SQL的id;
    // 參數二:和映射文件中匹配的parameterType參數
    // 返回值:映射文件中resultType配置的javabean
    User user = session.selectOne("test.findUserById", 1);
    System.out.println(user);
    
    // 釋放資源(關閉會話)
    session.close();
}

根據用戶信息模糊查詢用戶信息

在User.xml中配置以下:

<!-- 根據用戶名進行模糊查詢
    注意:雖然查詢的結果多是多條,可是resultType仍是javabean,即:單條記錄所對應的java對象
 -->
<select id="findUserByName" parameterType="java.lang.String" resultType="org.gpf.po.User">
    SELECT * FROM user WHERE username LIKE #{value};
</select>

測試類:

/**
 * 模糊查詢(可能返回多條)
 */
@Test
public void testFindUserByName() throws IOException {

    // 經過會話工廠獲得Sqlsession
    SqlSession session = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("SqlMapConfig.xml")).openSession();
    // 返回多條記錄
    List users = session.selectList("test.findUserByName", "%明%");
    System.out.println(users);
    session.close();
}

在執行模糊查詢的時候咱們在編碼的時候傳入了%做爲參數,爲了不出錯(少加了%)咱們能夠在User中配置如下的SQL(一樣:咱們須要注意:拼接SQL語句可能致使SQL注入):

<!-- 根據用戶名進行模糊查詢
    ${}            拼接SQL串,可能引發SQL注入
    ${value}       接收輸入的參數。若是是簡單類型,只能使用value
 -->
<select id="findUserByName" parameterType="java.lang.String" resultType="org.gpf.po.User">
    <!-- 使用${}進行SQL拼接 -->
    SELECT * FROM user WHERE username LIKE '%${value}%';
</select>

在測試代碼中咱們能夠這樣:

List users = session.selectList("test.findUserByName", "明");

由於${value}進行SQL拼接可能致使sql注入所以不建議使用。

添加用戶

在User.xml中配置Statement以下:

<!-- 添加用戶
    注意:此時輸入的參數名是POJO
    #{}            指定POJO的屬性值(mybatis經過OGNL來獲取屬性值)
 -->
<insert id="insertUser" parameterType="org.gpf.po.User">
    INSERT INTO user(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address});
</insert>

測試類:

/**
 * 添加用戶
 */
@Test
public void testInsertUser() throws IOException{
    
    // 獲得sqlsession
    SqlSession session = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("SqlMapConfig.xml")).openSession();
    
    // 實例化javabean
    User user = new User();
    user.setUsername("蕭晚晴");
    user.setBirthday(new Date());
    user.setSex("2");
    user.setAddress("峨眉山");
    
    // 執行插入語句,並提交事務
    session.insert("test.insertUser",user);
    session.commit();
    
    // 關閉sqlsession
    session.close();
}

自增主鍵的返回

MySQL自增主鍵,執行insert提交以前自動生成一個自增主鍵,經過其LAST_INSERT_ID()函數能夠得到其插入後的自增主鍵。修改以上的插入用戶的statement:

<insert id="insertUser" parameterType="org.gpf.po.User">
    <!-- 將INSERT插入記錄後的主鍵返回,SELECT  LAST_INSERT_ID()僅適用於自增主鍵
        keyProperty         將查詢到的主鍵值設置到parameterType指定對象的特定屬性
        order                執行順序(相對於INSERT語句來講)
        resultType            指定LAST_INSERT_ID()的結果類型
    -->
    <selectKey keyProperty="id" order="AFTER" resultType="int">
         SELECT  LAST_INSERT_ID();
    </selectKey>
    INSERT INTO user(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address});
</insert>

這樣咱們就能夠在執行插入以後取得剛剛插入的記錄的主鍵:

/**
 * 添加用戶
 */
@Test
public void testInsertUser() throws IOException{
    
    // 獲得sqlsession
    SqlSession session = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("SqlMapConfig.xml")).openSession();
    
    // 實例化javabean
    User user = new User();
    user.setUsername("蕭晚晴");
    user.setBirthday(new Date());
    user.setSex("2");
    user.setAddress("峨眉山");
    
    // 執行插入語句,並提交事務
    session.insert("test.insertUser",user);
    session.commit();
    System.out.println(user.getId());    // 獲得插入數據的主鍵
    // 關閉sqlsession
    session.close();
}

非自增主鍵的返回

可使用MySQL的uuid()函數來生成主鍵,須要修改表的id字段爲String,長度設置爲35位。執行思路是先經過uuid()查詢到主鍵,將主鍵輸入到SQL中。注意:執行的uuid()函數的順序相對於INSERT語句以前執行。

<!-- 使用MySQL的uuid()返回主鍵,該方法適用於非自增主鍵-->
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
     SELECT  UUID();
</selectKey>
INSERT INTO user(id,username,birthday,sex,address) VALUES(#{id},#{username},#{birthday},#{sex},#{address});

若是使用的是Oracle,序列就相似於MySQL的uuid()函數,替換便可:

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

刪除用戶

映射文件:

<!-- 刪除用戶 -->
<delete id="deleteUser" parameterType="java.lang.Integer">
    DELETE FROM user WHERE id = #{id}
</delete>

測試類:

/**
 * 根據id刪除用戶
 */
@Test
public void testDeleteUser() throws IOException{
    // 獲得sqlsession
    SqlSession session = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("SqlMapConfig.xml")).openSession();
    
    // 執行刪除語句,並提交事務
    session.delete("test.deleteUser", 31);
    session.commit();
    // 關閉sqlsession
    session.close();
}

更新用戶信息

映射文件

<!-- 更新用戶信息 
    由於更新用戶信息須要指定用戶的id和用戶的信息,因此parameterType爲javabean(注意id必須存在不然將所有更新)
    #{id}          從輸入的user對象中獲取id屬性值
-->
<update id="updateUser" parameterType="org.gpf.po.User">
    UPDATE user SET username = #{username},birthday=#{birthday},sex=#{sex},address=#{address} WHERE id = #{id}
</update>

測試程序

/**
 * 更新用戶
 */
@Test
public void testupdateUser() throws IOException{
    // 獲得sqlsession
    SqlSession session = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("SqlMapConfig.xml")).openSession();
    
    // 實例化javabean
    User user = new User();
    user.setId(32);
    user.setUsername("上官飛燕");
    user.setBirthday(new Date());
    user.setSex("1");
    user.setAddress("青城山");
    
    // 執行更新語句,並提交事務
    session.update("test.updateUser",user);
    session.commit();
    // 關閉sqlsession
    session.close();
}

總結

  • parameterType
    在映射文件中經過parameterType指定輸入參數的類型。

  • resultType
    在映射文件中經過resultType指定輸出結果的類型。

  • #{}和${}

#{}表示一個佔位符號,#{}接收輸入參數,類型能夠是簡單類型,pojo、hashmap。若是接收簡單類型,#{}中能夠寫成value或其它名稱。
#{}接收pojo對象值,經過OGNL讀取對象中的屬性值,經過屬性.屬性.屬性...的方式獲取對象屬性值。

${}表示一個拼接符號,會引用sql注入,因此不建議使用${}。
${}接收輸入參數,類型能夠是簡單類型,pojo、hashmap。若是接收簡單類型,${}中只能寫成value。
${}接收pojo對象值,經過OGNL讀取對象中的屬性值,經過屬性.屬性.屬性...的方式獲取對象屬性值。

  • selectOne和selectList

    • selectOne表示查詢出一條記錄進行映射。若是使用selectOne能夠實現使用selectList也能夠實現(list中只有一個對象)。

    • selectList表示查詢出一個列表(多條記錄)進行映射。若是使用selectList查詢多條記錄,不能使用selectOne。

若是使用selectOne報錯:

org.apache.ibatis.exceptions.TooManyResultsException: Expected oneresult (or null) to be returned by selectOne(), but found: 4

相關文章
相關標籤/搜索