您好,登錄后才能下訂單哦!
微信公眾號【黃小斜】大廠程序員,互聯網行業新知,終身學習踐行者。關注后回復「Java」、「Python」、「C++」、「大數據」、「機器學習」、「算法」、「AI」、「Android」、「前端」、「iOS」、「考研」、「BAT」、「校招」、「筆試」、「面試」、「面經」、「計算機基礎」、「LeetCode」 等關鍵字可以獲取對應的免費學習資料。
2017-06-12
文章導航
Mybatis 和 SpringMVC 通過訂單商品案例驅動
官方中文地址: http://www.mybatis.org/mybatis-3/zh/
官方托管地址: https://github.com/mybatis/mybatis-3
本項目全部代碼地址: https://github.com/zhisheng17/mybatis
java 環境 :jdk1.8.0_77
開發工具 : IDEA 2016.1
數據庫 : MySQL 5.7
mybatis_test.sql
Tables :items、orderdetail、orders、user
使用 JDBC 查詢 MySQL 數據庫中用戶表的記錄
代碼:
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
package cn.zhisheng.mybatis.jdbc; /** * Created by 10412 on 2016/11/27. */ import java.sql.*; /** *通過單獨的jdbc程序來總結問題 */ public class JdbcTest { public static void main(String[] args) { //數據庫連接 Connection connection = null; //預編譯的Statement,使用預編譯的Statement可以提高數據庫性能 PreparedStatement preparedStatement = null; //結果集 ResultSet resultSet = null; try { //加載數據庫驅動 Class.forName("com.mysql.jdbc.Driver"); //通過驅動管理類獲取數據庫鏈接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis_test?characterEncoding=utf-8", "root", "root"); //定義sql語句 ?表示占位符(在這里表示username) String sql = "select * from user where username = ?"; //獲取預處理statement preparedStatement = connection.prepareStatement(sql); //設置參數,第一個參數為sql語句中參數的序號(從1開始),第二個參數為設置的參數值 preparedStatement.setString(1, "王五"); //向數據庫發出sql執行查詢,查詢出結果集 resultSet = preparedStatement.executeQuery(); //遍歷查詢結果集 while(resultSet.next()) { System.out.println(resultSet.getString("id")+" "+resultSet.getString("username")); } } catch (Exception e) { e.printStackTrace(); }finally{ //釋放資源 if(resultSet!=null) { try { resultSet.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(preparedStatement!=null) { try { preparedStatement.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(connection!=null) { try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } |
數據庫連接,使用時就創建,不使用立即釋放,對數據庫頻繁連接開啟和關閉,造成數據庫資源的浪費,影響數據庫性能。
解決方法:使用數據庫連接池管理數據庫連接。
將 sql 語句 硬編碼 到 java 代碼中,如果 sql 語句需要修改,那么就需要重新編譯 java 代碼,不利于系統的維護。
設想:將 sql 語句配置在 xml 配置文件中,即使 sql 語句發生變化,也不需要重新編譯 java 代碼。
向 preparedStatement 中設置參數,對占位符號位置和設置參數值,硬編碼在 java 代碼中,同樣也不利于系統的維護。
設想:將 sql 語句、占位符、參數值配置在 xml 配置文件中。
從 resultSet 中遍歷結果集數據時,存在硬編碼,將獲取表的字段進行硬編碼,不利于系統維護。
設想:將查詢的結果集自動映射成 java 對象。
Mybatis 是一個持久層的架構,是 appach 下的頂級項目。
Mybatis 原先是托管在 googlecode 下,再后來是托管在 Github 上。
Mybatis 讓程序員將主要的精力放在 sql 上,通過 Mybatis 提供的映射方式,自由靈活生成(半自動,大部分需要程序員編寫 sql )滿足需要 sql 語句。
Mybatis 可以將向 preparedStatement 中的輸入參數自動進行 輸入映射 ,將查詢結果集靈活的映射成 java 對象。( 輸出映射 )
注解:
SqlMapConfig.xml (Mybatis的全局配置文件,名稱不定)配置了數據源、事務等 Mybatis 運行環境
Mapper.xml 映射文件(配置 sql 語句)
SqlSessionFactory (會話工廠)根據配置文件配置工廠、創建 SqlSession
SqlSession (會話)面向用戶的接口、操作數據庫(發出 sql 增刪改查)
Executor (執行器)是一個接口(基本執行器、緩存執行器)、SqlSession 內部通過執行器操作數據庫
Mapped Statement (底層封裝對象)對操作數據庫存儲封裝,包括 sql 語句、輸入參數、輸出結果類型
實現以下功能:
- 根據用戶id查詢一個用戶信息
- 根據用戶名稱模糊查詢用戶信息列表
- 添加用戶
- 更新用戶
- 刪除用戶
java 環境 :jdk1.8.0_77
開發工具 : IDEA 2016.1
數據庫 : MySQL 5.7
Mybatis 運行環境( jar 包)
MySQL 驅動包
其他依賴包
在classpath下創建log4j.properties如下:
1 2 3 4 5 6 7 |
# Global logging configuration #在開發環境日志級別要設置為DEBUG、生產環境要設置為INFO或者ERROR 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默認使用log4j作為輸出日志信息。
配置 Mybatis 的運行環境、數據源、事務等
10 11 12 13 14 15 16 17 18 19 20 |
<?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管理,數據庫名是mybatis_test,Mysql用戶名root,密碼root --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_test?characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> </configuration> |
Po 類作為 mybatis 進行 sql 映射使用,po 類通常與數據庫表對應,User.java 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
package cn.zhisheng.mybatis.po; import java.util.Date; /** * Created by 10412 on 2016/11/28. */ public class User { private int id; private String username; // 用戶姓名 private String sex; // 性別 private Date birthday; // 生日 private String address; // 地址 //getter and setter public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } 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; } } |
映射文件
- User.xml(原在 Ibatis 中命名)在 Mybatis 中命名規則為 xxxmapper.xml
- 在映射文件中配置 sql 語句
User.xml
1 2 3 4 5 6 |
<?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="test"> </mapper> |
namespace
:命名空間,對 sql 進行分類化管理,用于隔離 sql 語句,后面會講另一層非常重要的作用。
在
User.xml
中加入
2 3 4 5 6 7 8 9 10 11 12 |
<!--通過select執行數據庫查詢 id:標識映射文件中的sql 將sql語句封裝到mappedStatement對象中,所以id稱為Statement的id #{}:表示占位符 #{id}:其中的id表示接收輸入的參數,參數名稱就是id,如果輸入參數是簡單類型,那么#{}中的參數名可以任意,可以是value或者其他名稱 parameterType:表示指定輸入參數的類型 resultType:表示指定sql輸出結果的所映射的java對象類型 --> <!-- 根據id獲取用戶信息 --> <select id="findUserById" parameterType="int" resultType="cn.zhisheng.mybatis.po.User"> select * from user where id = #{id} </select> |
User.xml
映射文件已經完全寫好了,那接下來就需要在
SqlMapConfig.xml
中加載映射文件
User.xml
1 2 3 4 |
<!--加載映射文件--> <mappers> <mapper resource="sqlmap/User.xml"/> </mappers> |
編寫程序
`MybatisFirst.java`
2 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package cn.zhisheng.mybatis.first; import cn.zhisheng.mybatis.po.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; /** * Created by 10412 on 2016/11/28. */ public class MybatisFirst { //根據id查詢用戶信息,得到用戶的一條記錄 @Test public void findUserByIdTest() throws IOException { //Mybatis 配置文件 String resource = "SqlMapConfig.xml"; //得到配置文件流 InputStream inputStream = Resources.getResourceAsStream(resource); //創建會話工廠,傳入Mybatis的配置文件信息 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //通過工廠得到SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); //通過SqlSession操作數據庫 //第一個參數:映射文件中Statement的id,等于 = namespace + "." + Statement的id //第二個參數:指定和映射文件中所匹配的parameterType類型的參數 //sqlSession.selectOne 結果與映射文件中所匹配的resultType類型的對象 User user = sqlSession.selectOne("test.findUserById", 1); System.out.println(user); //釋放資源 sqlSession.close(); } } |
然后運行一下這個測試,發現結果如下就代表可以了:
映射文件
依舊使用 User.xml 文件,只不過要在原來的文件中加入
1 2 3 4 5 6 7 8 9 |
<!-- 自定義條件查詢用戶列表 resultType:指定就是單條記錄所映射的java對象類型 ${}:表示拼接sql串,將接收到的參數內容不加修飾的拼接在sql中 使用${}拼接sql,會引起sql注入 ${value}:接收輸入參數的內容,如果傳入類型是簡單類型,${}中只能夠使用value --> <select id="findUserByUsername" parameterType="java.lang.String" resultType="cn.zhisheng.mybatis.po.User"> select * from user where username like '%${value}%' </select> |
編寫程序
依舊直接在剛才那個
MybatisFirst.java
中加入測試代碼:
3 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
//根據用戶名稱模糊查詢用戶信息列表 @Test public void findUserByUsernameTest() throws IOException { //Mybatis 配置文件 String resource = "SqlMapConfig.xml"; //得到配置文件流 InputStream inputStream = Resources.getResourceAsStream(resource); //創建會話工廠,傳入Mybatis的配置文件信息 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //通過工廠得到SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); //通過SqlSession操作數據庫 //第一個參數:映射文件中Statement的id,等于 = namespace + "." + Statement的id //第二個參數:指定和映射文件中所匹配的parameterType類型的參數 //selectList 查詢結果可能多條 //list中的user和映射文件中resultType所指定的類型一致 List<User> list = sqlSession.selectList("test.findUserByUsername", "小明"); System.out.println(list); //釋放資源 sqlSession.close(); } |
同樣測試一下
findUserByUsernameTest
,如果運行結果如下就代表沒問題:
通過這個代碼可以發現,其中有一部分代碼是冗余的,我們可以將其封裝成一個函數。
1 2 3 4 5 6 7 |
public void createSqlSessionFactory() throws IOException { // 配置文件 String resource = "SqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); // 使用SqlSessionFactoryBuilder從xml配置文件中創建SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } |
#{ }
表示一個占位符號,通過#{ }
可以實現preparedStatement
向占位符中設置值,自動進行java 類型和 jdbc 類型轉換,#{ }
可以有效防止sql注入。#{ }
可以接收簡單類型值或 pojo 屬性值(通過 OGNL 讀取對象中的值,屬性.屬性.屬性..方式獲取對象屬性值)。 如果parameterType
傳輸單個簡單類型值,#{ }
括號中可以是 value 或其它名稱。${ }
表示拼接 sql 串,通過${ }
可以將 parameterType 傳入的內容拼接在 sql 中且不進行 jdbc 類型轉換,${ }
可以接收簡單類型值或 pojo 屬性值((通過 OGNL 讀取對象中的值,屬性.屬性.屬性..方式獲取對象屬性值)),如果 parameterType 傳輸單個簡單類型值,${}括號中只能是 value。
- parameterType:指定輸入參數類型,mybatis 通過 ognl 從輸入對象中獲取參數值拼接在 sql 中。
- resultType:指定輸出結果類型,mybatis 將 sql 查詢結果的一行記錄數據映射為 resultType 指定類型的對象。
selectOne 查詢一條記錄來進行映射,如果使用selectOne查詢多條記錄則拋出異常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to bereturned by selectOne(), but found: 3 at
selectList 可以查詢一條或多條記錄來進行映射。
映射文件
在 User.xml 中加入:
1 2 3 4 5 6 7 8 |
<!-- 添加用戶 --> <insert id="insetrUser" parameterType="cn.zhisheng.mybatis.po.User" > <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> select LAST_INSERT_ID() </selectKey> insert into user(username, birthday, sex, address) values(#{username}, #{birthday}, #{sex}, #{address}) </insert> |
注意:
- selectKey將主鍵返回,需要再返回
- 添加selectKey實現將主鍵返回
- keyProperty:返回的主鍵存儲在pojo中的哪個屬性
- order:selectKey的執行順序,是相對與insert語句來說,由于mysql的自增原理執行完insert語句之后才將主鍵生成,所以這里selectKey的執行順序為after
- resultType:返回的主鍵是什么類型
- LAST_INSERT_ID():是mysql的函數,返回auto_increment自增列新記錄id值。
然后在
MybatisFirst.java
中寫一個測試函數,代碼如下
11 12 13 14 15 16 17 18 19 20 21 |
@Test public void insetrUser() throws IOException, ParseException { //Mybatis 配置文件 String resource = "SqlMapConfig.xml"; //得到配置文件流 InputStream inputStream = Resources.getResourceAsStream(resource); //創建會話工廠,傳入Mybatis的配置文件信息 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //通過工廠得到SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); User user = new User(); SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd"); user.setUsername("田志聲"); user.setSex("男"); user.setBirthday(sdf.parse("2016-11-29")); user.setAddress("江西南昌"); sqlSession.insert("test.insetrUser", user); sqlSession.commit(); //釋放資源 sqlSession.close(); } |
然后 run 一下,如果出現的結果如下,那么就是成功了。
同時數據庫也能查詢到剛插入的用戶信息:
MySQL 自增主鍵:執行 insert 提交之前自動生成一個自增主鍵,通過 MySQL 函數獲取到剛插入記錄的自增主鍵: LAST_INSERT_ID() ,是在 insert 函數之后調用。
非自增主鍵返回:使用 MySQL 的 uuid() 函數生成主鍵,需要修改表中 id 字段類型為 String ,長度設置為 35 位,執行思路:先通過 uuid() 查詢到主鍵,將主鍵輸入到 sql 語句中;執行 uuid() 語句順序相對于 insert 語句之前執行。
剛才那個插入用戶的地方,其實也可以通過 uuid() 來生成主鍵,如果是這樣的話,那么我們就需要在
User.xml
中加入如下代碼:
1 2 3 4 5 6 7 8 9 |
<!--使用 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) values(#{id}, #{username}, #{birthday}, #{sex}, #{address}) |
Oracle 使用序列生成主鍵
首先自定義一個序列且用于生成主鍵,selectKey使用如下:
1 2 3 4 5 6 7 8 |
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User"> <selectKey resultType="java.lang.Integer" order="BEFORE" keyProperty="id"> SELECT 自定義序列.NEXTVAL FROM DUAL </selectKey> insert into user(id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address}) </insert> |
前面說了這么多了,這里就簡單來說明下:
在 User.xml 文件中加入如下代碼:
1 2 3 4 |
<!--刪除用戶--> <delete id="deleteUserById" parameterType="int"> delete from user where user.id = #{id} </delete> |
在 MybatisFirst.java 文件中加入如下代碼:
4 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
//刪除用戶 @Test public void deleteUserByIdTest() throws IOException { //Mybatis 配置文件 String resource = "SqlMapConfig.xml"; //得到配置文件流 InputStream inputStream = Resources.getResourceAsStream(resource); //創建會話工廠,傳入Mybatis的配置文件信息 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //通過工廠得到SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); //通過SqlSession操作數據庫 //第一個參數:映射文件中Statement的id,等于 = namespace + "." + Statement的id //第二個參數:指定和映射文件中所匹配的parameterType類型的參數 sqlSession.delete("test.deleteUserById", 26); //提交事務 sqlSession.commit(); //釋放資源 sqlSession.close(); } |
測試結果如下:
之前的數據庫 user 表查詢結果:
執行完測試代碼后,結果如下:
在 User.xml 中加入如下代碼:
1 2 3 4 5 6 7 8 9 |
<!--根據id更新用戶 需要輸入用戶的id 傳入用戶要更新的信息 parameterType指定user對象,包括id和更新信息,id必須存在 #{id}:從輸入對象中獲取id屬性值 --> <update id="updateUserById" parameterType="cn.zhisheng.mybatis.po.User"> update user set username = #{username}, birthday = #{birthday}, sex = #{sex}, address = #{address} where user.id = #{id} </update> |
然后在 MybatisFirst.java 中加入
8 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
//根據id更新用戶信息 @Test public void updateUserByIdTest() throws IOException, ParseException { //Mybatis 配置文件 String resource = "SqlMapConfig.xml"; //得到配置文件流 InputStream inputStream = Resources.getResourceAsStream(resource); //創建會話工廠,傳入Mybatis的配置文件信息 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //通過工廠得到SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); //為了設置生日的日期輸入 SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd"); User user = new User(); //根據id更新用戶信息 user.setId(24); user.setUsername("張四風"); user.setBirthday(sdf.parse("2015-01-12")); user.setSex("女"); user.setAddress("上海黃埔"); //通過SqlSession操作數據庫 //第一個參數:映射文件中Statement的id,等于 = namespace + "." + Statement的id //第二個參數:指定和映射文件中所匹配的parameterType類型的參數 sqlSession.update("test.updateUserById", user); //提交事務 sqlSession.commit(); //釋放資源 sqlSession.close(); } |
測試結果如下:
查看數據庫,id 為 24 的用戶信息是否更新了:
啊,是不是很爽,所有的需求都完成了。
沒錯,這只是 Mybatis 的一個簡單的入門程序,簡單的實現了對數據庫的增刪改查功能,通過這個我們大概可以了解這個編程方式了。
期待接下來的 Mybatis高級知識文章吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。