mybatis 經過 簡單的xml 或註解的方式將要執行的各類 statement 配置起來,並經過 java 對象(POJO類)和 statement 中sql 的動態參數進行映射生成最終執行的 sql 語句,最後由 mybatis 框架執行 sql 並將結果映射爲 java 對象並返回。java
1.未經封裝,使用jdbc原始方法(加載驅動,獲取鏈接)進行對數據庫數據的crud操做mysql
2.數據庫鏈接頻繁建立、釋放,形成系統資源的浪費,影響系統性能,web
解決:可以使用數據庫鏈接池技術
3.Sql語句在代碼中屬於硬編碼,而實際應用sql語句常常變化,需改變java代碼,不利於代碼維護sql
解決:將 Sql 語句配置在 XXXXmapper.xml 文件中與 java 代碼分離。
4.使用prepareStatement向佔位符傳參存在硬編碼,由於參數不定,條件可多可少,需修改代碼不易維護。數據庫
解決:Mybatis 自動將 java 對象映射至 sql 語句,經過 statement 中的 parameterType 定義輸入參數的類型。
5.結果集解析存在硬編碼(查詢列名),若sql變化(參數),致使解析代碼變化,系統不易維護,可將數據庫記錄封裝爲POJO類對象。windows
解決:Mybatis 自動將 sql 執行結果映射至 java 對象,經過 statement 中的 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"> <!--namespace屬性爲dao接口的全限定類名--> <mapper namespace="com.itheima.dao.IUserDao"> <!--配置查詢全部--> <select id="findAll" resultType="com.itheima.domain.User"> select * from user </select> </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"> <!-- mybatis的主配置文件 --> <configuration> <!-- 配置環境 --> <environments default="mysql"> <!-- 配置mysql的環境--> <environment id="mysql"> <!-- 配置事務的類型--> <transactionManager type="JDBC"></transactionManager> <!-- 配置數據源(鏈接池) --> <dataSource type="POOLED"> <!-- 配置鏈接數據庫的4個基本信息,建立鏈接對象 --> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/> <property name="username" value="root"/> <property name="password" value="1234"/> </dataSource> </environment> </environments> <!-- 指定映射配置文件的位置,映射配置文件指的是每一個dao獨立的配置文件 --> <mappers> <!-- 基於xml --> <mapper resource="com/itheima/dao/IUserDao.xml"/> <!-- 基於註解 --> <mapper class="com.itheima.dao.IUserDao.xml"/> </mappers> </configuration>
public class MybatisTest { /** * 入門案例 * @param args */ public static void main(String[] args)throws Exception { //1.讀取配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.建立SqlSessionFactory工廠(不是本身建立的,構建者模式) SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in); //3.使用工廠生產SqlSession對象 SqlSession session = factory.openSession(); //4.使用SqlSession建立Dao接口的代理對象 IUserDao userDao = session.getMapper(IUserDao.class); //5.使用代理對象執行方法 List<User> users = userDao.findAll(); for(User user : users){ System.out.println(user); } //6.釋放資源 session.close(); in.close(); } }
第一個:使用類加載器,只能讀取類路徑的配置文件設計模式
第二個:使用servletContext對象的getRealPath()緩存
優點:把對象的建立細節隱藏,使使用者直接調用方法便可拿到對象安全
優點:解耦,下降類之間的依賴。使用工廠生產對象,解決類之間的依賴,web開發每次修改改動源碼,影響開發效率,使用工廠生產對象,不須要從新編譯,部署,啓動服務器。服務器
優點:在不修改源碼基礎上對已有方法進行加強
加強對象的功能:
設計模式:一些通用的解決固定問題的方式
1)裝飾模式
2)代理模式
靜態代理:有一個類文件描述代理模式
動態代理:在內存中造成代理類
tips:使用jdbc鏈接數據庫執行sql語句時(執行許多SQL語句的JDBC程序產生大量的Statement和PreparedStatement對象。)
1.當sql語句含有多個參數,屢次使用時,使用preparestament將sql進行初始化,提到數據庫中進行預編譯,提升效率;
2.能夠替換變量,sql語句能夠包含佔位符?,把?替換成變量,ps.setString()....
3.安全性,有效防止SQL注入的問題:傳遞給PreparedStatement對象的參數能夠被強制進行類型轉換,使開發人員能夠確保在插入或查詢數據時與底層的數據庫格式匹配。(不太懂)
總結:
PreparedStatement: 數據庫會對sql語句進行預編譯,下次執行相同的sql語句時,數據庫端不會再進行預編譯了,而直接用數據庫的緩衝區,提升數據訪問的效率(但儘可能採用使用?號的方式傳遞參數),若是sql語句只執行一次,之後再也不復用。 從安全性上來看,PreparedStatement是經過?來傳遞參數的,避免了拼sql而出現sql注入的問題,因此安全性較好。
在開發中,推薦使用 PreparedStatement
所謂SQL注入,就是經過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的SQL命令。具體來講,它是利用現有應用程序,將(惡意)的SQL命令注入到後臺數據庫引擎執行的能力,它能夠經過在Web表單中輸入(惡意)SQL語句獲得一個存在安全漏洞的網站上的數據庫,而不是按照設計者意圖去執行SQL語句。
怎麼防止SQL注入,使用存儲過程來執行全部的查詢;檢查用戶輸入的合法性;將用戶的登陸名、密碼等數據加密保存。
至關於佔位符,裏面內容爲基本類型時隨意寫,若參數爲對象,則#{}裏使用ognl(對象圖導航語言)表達式,語法格式爲#{對象.對象}的方式
{user.username}它會先去找 user 對象,而後在 user 對象中找到 username 屬性,並調用getUsername()方法把值取出來。可是咱們在 parameterType 屬性上指定了實體類名稱,因此能夠省略 user.而直接寫 username。
添加記錄,使用insert標籤。只有參數,沒有返回值。
若是Mysql數據想獲取當前記錄的ID,須要配置:
selectKey
當運行了添加的方法後,mybatis會自動將返回的ID,設置給參數對象parameterType配置的對象
<insert id="saveUser" parameterType="com.itheima.domain.User"> <!-- 配置插入操做後,獲取插入數據的id --> <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER"> select last_insert_id(); </selectKey> insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday}); </insert>
咱們在配置文件中沒有加入%來做爲模糊查詢的條件,因此在傳入字符串實參時,就須要給定模糊查詢的標
識%。 配置文件中的#{username}也只是一個佔位符,因此 SQL 語句顯示爲「?」。
模糊查詢另外一種查詢方式:'%${value}%' 代替#{}
#{}表示一個佔位符號 經過#{}能夠實現 preparedStatement 向佔位符中設置值,自動進行 java 類型和 jdbc 類型轉換, #{}能夠有效防止 sql 注入。 #{}能夠接收簡單類型值或 pojo 屬性值。 若是 parameterType 傳輸單個簡單類 型值, #{}括號中能夠是 value 或其它名稱。 ${}表示拼接 sql 串 經過${}能夠將 parameterType 傳入的內容拼接在 sql 中且不進行 jdbc 類型轉換, ${}能夠接收簡 單類型值或 pojo 屬性值,若是 parameterType 傳輸單個簡單類型值, ${}括號中只能是 value。
parameterType:基本類型與string,採用包名.類名,如Java.Lang.String
包裝類採用全限定分類名
特殊狀況:實體類屬性名稱與數據庫列名不一致,查詢結果爲null
MySql在windows系統中不區分大小寫,映射配置修改以下:
<!-- 配置查詢全部操做 --> <select id="findAll" resultType="com.itheima.domain.User"> select * from user </select>
改成:
使用別名查詢 <!-- 配置查詢全部操做 --> <select id="findAll" resultType="com.itheima.domain.User"> select id as userId,username as userName,birthday as userBirthday, sex as userSex,address as userAddress from user </select>
思考:
若是咱們的查詢不少,都使用別名的話寫起來豈不是很麻煩,有沒有別的解決辦法呢? 以下
<!-- 創建 User 實體和數據庫表的對應關係 type 屬性:指定實體類的全限定類名 id 屬性:給定一個惟一標識,是給查詢 select 標籤引用用的。 --> <resultMap type="com.itheima.domain.User" id="userMap"> <id column="id" property="userId"/> <result column="username" property="userName"/> <result column="sex" property="userSex"/> <result column="address" property="userAddress"/> <result column="birthday" property="userBirthday"/> </resultMap> id 標籤:用於指定主鍵字段 result 標籤:用於指定非主鍵字段 column 屬性:用於指定數據庫列名 property 屬性:用於指定實體類屬性名稱
對應映射配置
<!-- 配置查詢全部操做 --> <select id="findAll" resultMap="userMap"> select * from user </select>
properties標籤:需把jdbcConfig.properties提取出來,放到resources路徑下
typeAliases標籤:
pakage標籤:
<!-- mybatis的主配置文件 --> <configuration> <properties resource="jdbcConfig.properties"></properties> <!--使用typeAliases配置別名,它只能配置domain中類的別名 --> <typeAliases> <!-- 用於指定要配置別名的包,當指定以後,該包下的實體類都會註冊別名,而且類名就是別名,再也不區分大小寫--> <package name="com.itheima.domain"></package> </typeAliases> <!-- 配置環境 --> <environments default="mysql"> <!-- 配置mysql的環境--> <environment id="mysql"> <!-- 配置事務的類型--> <transactionManager type="JDBC"></transactionManager> <!-- 配置數據源(鏈接池) --> <dataSource type="POOLED"> <!-- 配置鏈接數據庫的4個基本信息 --> <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> <!-- 指定映射配置文件的位置,映射配置文件指的是每一個dao獨立的配置文件 --> <mappers> <!-- package標籤是用於指定dao接口所在的包,當指定了以後就不須要在寫mapper以及resource或者class了 --> <package name="com.itheima.dao"></package> </mappers> </configuration>
動態SQL目的是爲了更好的重用SQL語句的片斷或者靈活的生成SQL語句。本質上,就是SQL語句字符串的拼接。
if進行判斷,test屬性爲true,就拼接上標籤中的SQL語句 test中就是OGNL表達式 1. 不要加 #{} 2. 邏輯運算,要使用 and,or 3. 調用對象的方法: list.size() <select id="findUserByCondition" resultMap="userMap" parameterType="user"> select * from user <where> <if test="userName != null"> and username = #{userName} </if> <if test="userSex != null"> and sex = #{userSex} </if> </where> </select>
<update id="updateUser" parameterType="user"> update user <set> <if test="username !=null and username !=''"> username = #{username} , </if> <if test="username !=null"> sex = #{sex}, </if> <if test="username !=null"> password = #{password}, </if> </set> where id=#{id} </update>
foreach 進行循環的集合,必須經過包裝的對象提供。 <select id="findInIds" resultType="user" parameterType="queryvo"> <where> <if test="ids != null and ids.size() > 0"> <foreach collection="ids" open="and id in (" close=")" item="uid" separator=","> #{uid} </foreach> </if> </where> </select>
將SQL中,相同的內容提取出來,在多個地方進行引用。 1. SQL語句中的字符串 2. 動態SQL中的標籤 抽取SQL語句 <sql id="defaultUser"> select * from user </sql> 使用SQL <select id="findAll" resultMap="userMap"> <include refid="defaultUser"></include> </select>
簡化sql片斷
<!-- 抽取重複的語句代碼片斷 --> <sql id="defaultSql"> select * from user </sql> <!-- 配置查詢全部操做 --> <select id="findAll" resultType="user"> <include refid="defaultSql"></include> </select>
使用 resultMap,定義專門的 resultMap 用於映射一對一查詢結果。
經過面向對象的(has a)關係能夠得知,咱們能夠在 Account 類中加入一個 User 類的對象來表明這個帳戶
是哪一個用戶的。
<resultMap id="accountUserMap" type="account"> <id property="id" column="aid"></id> <result property="uid" column="uid"></result> <result property="money" column="money"></result> <!-- 一對一的關係映射:配置封裝user的內容--> <association property="user" column="uid" javaType="user"> <id property="id" column="id"></id> <result column="username" property="username"></result> <result column="address" property="address"></result> <result column="sex" property="sex"></result> <result column="birthday" property="birthday"></result> </association> </resultMap>
<resultMap type="user" id="userMap"> <id column="id" property="id"></id> <result column="username" property="username"/> <result column="address" property="address"/> <result column="sex" property="sex"/> <result column="birthday" property="birthday"/> <!-- collection 是用於創建一對多中集合屬性的對應關係 ofType 用於指定集合元素的數據類型 --> <collection property="accounts" ofType="account"> <id column="aid" property="id"/> <result column="uid" property="uid"/> <result column="money" property="money"/> </collection> </resultMap> <!-- 配置查詢全部操做 --> <select id="findAll" resultMap="userMap"> select u.*,a.id as aid ,a.uid,a.money from user u left outer join account a on u.id =a.uid </select> </mapper> collection 部分定義了用戶關聯的帳戶信息。表示關聯查詢結果集 property="accList": 關聯查詢的結果集存儲在 User 對象的上哪一個屬性。 ofType="account": 指定關聯查詢的結果集中的對象類型即 List中的對象類型。此處可使用別名,也可使用全限定名。
<!--定義 role 表的 ResultMap--> <resultMap id="roleMap" type="role"> <id property="roleId" column="rid"></id> <result property="roleName" column="role_name"></result> <result property="roleDesc" column="role_desc"></result> <collection property="users" ofType="user"> <id column="id" property="id"></id> <result column="username" property="username"></result> <result column="address" property="address"></result> <result column="sex" property="sex"></result> <result column="birthday" property="birthday"></result> </collection> </resultMap> <!--查詢全部--> <select id="findAll" resultMap="roleMap"> select u.*,r.id as rid,r.role_name,r.role_desc from role r left outer join user_role ur on r.id = ur.rid left outer join user u on u.id = ur.uid </select> </mapper>
//SqlMapConfig.xml <!--配置參數--> <settings> <!--開啓Mybatis支持延遲加載--> <setting name="lazyLoadingEnabled" value="true"/> <!-- 默認就是true --> <setting name="aggressiveLazyLoading" value="false"></setting> </settings>
2.一對一
<!-- 一對一的關係映射:配置封裝user的內容 select屬性指定的內容:查詢用戶的惟一標識:當前mapper配置的sid(namespace+id) column屬性指定的內容:用戶根據id查詢時,所須要的參數的值 --> <association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById"></association> <select id="findAll" resultMap="accountMap"> select * from account </select> select: 填寫咱們要調用的 select 映射的 id column : 填寫咱們要傳遞給 select 映射的參數
3.一對多
<!-- collection 是用於創建一對多中集合屬性的對應關係 ofType 用於指定集合元素的數據類型 select 是用於指定查詢帳戶的惟一標識(帳戶的 dao 全限定類名加上方法名稱) column 是用於指定使用哪一個字段的值做爲條件查詢 --> <collection property="accounts" ofType="account" select="com.itheima.dao.IAccountDao.findByUid" column="id"> </collection> <!-- 配置查詢全部操做 --> <select id="findAll" resultMap="userMap"> select * from user </select> <collection>標籤: 主要用於加載關聯的集合對象 select 屬性: 用於指定查詢 account 列表的 sql 語句,因此填寫的是該 sql 映射的 id column 屬性: 用於指定 select 屬性的 sql 語句的參數來源,上面的參數來自於 user 的 id 列,因此就寫成 id 這一 個字段名了
緩存的使用
緩存的做用和緩存使用的時機
mybatis的緩存
一級緩存
SqlSession範圍的緩存,同一個SQLSession對象,可使用同一個緩存。當使用session查詢相同的數據(同一方法,相同參數),查詢出同一個對象。
當session關閉,調用clearCache()方法,執行增刪改操做時,會被清空。
二級緩存
SQLSessionFactory範圍的緩存,通常應用只會建立一個SQLSessionFactory對象,當前應用中,全部的操做均可以使用二級緩存的數據。不一樣的session查詢相同的數據(同一方法,相同參數)能夠從二級緩存中獲取,二級緩存只緩存了數據的內容,每一個session會拿到不一樣的對象,擁有相同的屬性。
二級緩存默認是不啓用的,須要手動開啓。
//SqlMapConfig.xml <settings> <setting name="cacheEnabled" value="true"/> </settings> //對應的mapper文件中 <cache /> //當前配置的mapper文件的對應須要緩存的SQL上 <!-- 根據id查詢用戶 --> <select id="findById" parameterType="INT" resultType="user" useCache="true"> select * from user where id = #{uid} </select>
@Insert:實現新增
@Update:實現更新
@Delete:實現刪除
@Select:實現查詢
@Result:實現結果集封裝
@Results:能夠與@Result 一塊兒使用,封裝多個結果集
@ResultMap:實現引用@Results 定義的封裝
@One:實現一對一結果集封裝
@Many:實現一對多結果集封裝
@SelectProvider: 實現動態 SQL 映射
@CacheNamespace:實現註解二級緩存的使用
搭建開發環境
若是使用了註解開發,就不能再使用xml配置。
單表增刪改查操做
對象屬性和表字段不一致的配置
@Select("select * from user") @Results(id="userMap",value={ @Result(id=true,column = "id",property = "userId"), @Result(column = "username",property = "userName"), @Result(column = "address",property = "userAddress"), @Result(column = "sex",property = "userSex"), @Result(column = "birthday",property = "userBirthday") }) List<User> findAll();
//重複使用resultmap @Select("select * from user where id=#{id} ") @ResultMap("userMap") User findById(Integer userId);
4.對象關係映射配置
一對一
@Select("select * from account") @Results(id="accountMap",value = { @Result(id=true,column = "id",property = "id"), @Result(column = "uid",property = "uid"), @Result(column = "money",property = "money"), @Result(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER)) }) //property 對象屬性的名稱 //column 後面配置的select查詢的參數 //one 屬性是對象 // select 查詢的方法 // fetchType 是否延遲加載 FetchType.EAGER 當即加載 List<Account> findAll();
2.一對多
@Select("select * from user") @Results(id="userMap",value={ @Result(id=true,column = "id",property = "userId"), @Result(column = "username",property = "userName"), @Result(column = "address",property = "userAddress"), @Result(column = "sex",property = "userSex"), @Result(column = "birthday",property = "userBirthday"), @Result(property = "accounts",column = "id", many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid", fetchType = FetchType.LAZY)) }) // many 對象的屬性是集合 fetchType = FetchType.LAZY 延遲就加載 List<User> findAll();