public static void main(String[] args) { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { //加載數據庫驅動 Class.forName("com.mysql.jdbc.Driver"); //經過驅動管理類獲取數據庫連接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "mysql"); //定義sql語句 ?表示佔位符 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(); } } } }
上邊使用jdbc的原始方法(未經封裝)實現了查詢數據庫表記錄的操做。前端
MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,而且更名爲MyBatis,實質上Mybatis對ibatis進行一些改進。java
MyBatis是一個優秀的持久層框架,它對jdbc的操做數據庫的過程進行封裝,使開發者只須要關注 SQL 自己,而不須要花費精力去處理例如註冊驅動、建立connection、建立statement、手動設置參數、結果集檢索等jdbc繁雜的過程代碼。mysql
Mybatis經過xml或註解的方式將要執行的各類statement(statement、preparedStatemnt、CallableStatement)配置起來,並經過java對象和statement中的sql進行映射生成最終執行的sql語句,最後由mybatis框架執行sql並將結果映射成java對象並返回。git
SqlMapConfig.xml,此文件做爲mybatis的全局配置文件,配置了mybatis的運行環境等信息。程序員
mapper.xml文件即sql映射文件,文件中配置了操做數據庫的sql語句。此文件須要在SqlMapConfig.xml中加載。github
mybaits的代碼由github.com管理,地址:https://github.com/mybatis/mybatis-3/releasesredis
mybatis-3.2.7.jar----mybatis的核心包spring
lib----mybatis的依賴包sql
mybatis-3.2.7.pdf----mybatis使用手冊數據庫
先導入sql_table.sql,再導入 sql_data.sql腳本:
以下:
實現如下功能:
根據用戶id查詢一個用戶信息
根據用戶名稱模糊查詢用戶信息列表
添加用戶
更新用戶
刪除用戶
使用eclipse建立java工程,jdk使用1.7.0_72。
加入mybatis核心包、依賴包、數據驅動包。
在classpath下建立log4j.properties以下:
# 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默認使用log4j做爲輸出日誌信息。
在classpath下建立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> <!-- 和spring整合後 environments配置將廢除--> <environments default="development"> <environment id="development"> <!-- 使用jdbc事務管理--> <transactionManager type="JDBC" /> <!-- 數據庫鏈接池--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url"value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/> <property name="username" value="root" /> <property name="password" value="mysql" /> </dataSource> </environment> </environments> </configuration>
SqlMapConfig.xml是mybatis核心配置文件,上邊文件的配置內容爲數據源、事務管理。
Po類做爲mybatis進行sql映射使用,po類一般與數據庫表對應,User.java以下:
public class User { private int id; private String username;// 用戶姓名 private String sex;// 性別 private Date birthday;// 生日 private String address;// 地址 get/set……
在classpath下的sqlmap目錄下建立sql映射文件Users.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="test"> </mapper>
namespace :命名空間,用於隔離sql語句,後面會講另外一層很是重要的做用。
在SqlMapConfig.xml中添加:
<!-- 根據id獲取用戶信息 --> <select id="findUserById" parameterType="int"resultType="cn.itcast.mybatis.po.User"> select * from user where id = #{id} </select> <!-- 自定義條件查詢用戶列表 --> <select id="findUserByUsername" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User"> select * from user where username like '%${value}%' </select>
parameterType:定義輸入到sql中的映射類型,#{id}表示使用preparedstatement設置佔位符號並將輸入變量id傳到sql。
resultType:定義結果映射類型。
mybatis框架須要加載映射文件,將Users.xml添加在SqlMapConfig.xml,以下:
<mappers> <mapper resource="sqlmap/User.xml"/> </mappers>
public class Mybatis_first { //會話工廠 private SqlSessionFactory sqlSessionFactory; @Before public void createSqlSessionFactory() throws IOException { // 配置文件 String resource = "SqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); // 使用SqlSessionFactoryBuilder從xml配置文件中建立SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() .build(inputStream); } // 根據 id查詢用戶信息 @Test public void testFindUserById() { // 數據庫會話實例 SqlSession sqlSession = null; try { // 建立數據庫會話實例sqlSession sqlSession = sqlSessionFactory.openSession(); // 查詢單個記錄,根據用戶id查詢用戶信息 User user = sqlSession.selectOne("test.findUserById", 10); // 輸出用戶信息 System.out.println(user); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } // 根據用戶名稱模糊查詢用戶信息 @Test public void testFindUserByUsername() { // 數據庫會話實例 SqlSession sqlSession = null; try { // 建立數據庫會話實例sqlSession sqlSession = sqlSessionFactory.openSession(); // 查詢單個記錄,根據用戶id查詢用戶信息 List<User> list = sqlSession.selectList("test.findUserByUsername", "張"); System.out.println(list.size()); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } }
#{}表示一個佔位符號,經過#{}能夠實現preparedStatement向佔位符中設置值,自動進行java類型和jdbc類型轉換,#{}能夠有效防止sql注入。 #{}能夠接收簡單類型值或pojo屬性值。 若是parameterType傳輸單個簡單類型值,#{}括號中能夠是value或其它名稱。
${}表示拼接sql串,經過${}能夠將parameterType 傳入的內容拼接在sql中且不進行jdbc類型轉換, ${}能夠接收簡單類型值或pojo屬性值,若是parameterType傳輸單個簡單類型值,${}括號中只能是value。
parameterType:指定輸入參數類型,mybatis經過ognl從輸入對象中獲取參數值拼接在sql中。
resultType:指定輸出結果類型,mybatis將sql查詢結果的一行記錄數據映射爲resultType指定類型的對象。
selectOne查詢一條記錄,若是使用selectOne查詢多條記錄則拋出異常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)
selectList能夠查詢一條或多條記錄。
在SqlMapConfig.xml中添加:
<!-- 添加用戶 --> <insert id="insertUser"parameterType="cn.itcast.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>
// 添加用戶信息 @Test public void testInsert() { // 數據庫會話實例 SqlSession sqlSession = null; try { // 建立數據庫會話實例sqlSession sqlSession = sqlSessionFactory.openSession(); // 添加用戶信息 User user = new User(); user.setUsername("張小明"); user.setAddress("河南鄭州"); user.setSex("1"); user.setPrice(1999.9f); sqlSession.insert("test.insertUser", user); //提交事務 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } }
經過修改sql映射文件,能夠將mysql自增主鍵返回:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User"> <!-- selectKey將主鍵返回,須要再返回 --> <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實現將主鍵返回
keyProperty:返回的主鍵存儲在pojo中的哪一個屬性
order:selectKey的執行順序,是相對與insert語句來講,因爲mysql的自增原理執行完insert語句以後纔將主鍵生成,因此這裏selectKey的執行順序爲after
resultType:返回的主鍵是什麼類型
LAST_INSERT_ID():是mysql的函數,返回auto_increment自增列新記錄id值。
須要增長經過select uuid()獲得uuid值
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey resultType="java.lang.String" order="BEFORE"
keyProperty="id">
select uuid()
</selectKey>
insert into user(id,username,birthday,sex,address)
values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
注意這裏使用的order是「BEFORE」
首先自定義一個序列且用於生成主鍵,selectKey使用以下:
<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>
注意這裏使用的order是「BEFORE」
<!-- 刪除用戶 --> <delete id="deleteUserById" parameterType="int"> delete from user where id=#{id} </delete>
// 根據id刪除用戶 @Test public void testDelete() { // 數據庫會話實例 SqlSession sqlSession = null; try { // 建立數據庫會話實例sqlSession sqlSession = sqlSessionFactory.openSession(); // 刪除用戶 sqlSession.delete("test.deleteUserById",18); // 提交事務 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } }
<!-- 更新用戶 --> <update id="updateUser"parameterType="cn.itcast.mybatis.po.User"> update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id} </update>
// 更新用戶信息 @Test public void testUpdate() { // 數據庫會話實例 SqlSession sqlSession = null; try { // 建立數據庫會話實例sqlSession sqlSession = sqlSessionFactory.openSession(); // 添加用戶信息 User user = new User(); user.setId(16); user.setUsername("張小明"); user.setAddress("河南鄭州"); user.setSex("1"); user.setPrice(1999.9f); sqlSession.update("test.updateUser", user); // 提交事務 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } }
解決:在SqlMapConfig.xml中配置數據連接池,使用鏈接池管理數據庫連接。
解決:將Sql語句配置在XXXXmapper.xml文件中與java代碼分離。
解決:Mybatis自動將java對象映射至sql語句,經過statement中的parameterType定義輸入參數的類型。
解決:Mybatis自動將sql執行結果映射至java對象,經過statement中的resultType定義輸出結果的類型。
Mybatis和hibernate不一樣,它不徹底是一個ORM框架,由於MyBatis須要程序員本身編寫Sql語句,不過mybatis能夠經過XML或註解方式靈活配置要運行的sql語句,並將java對象和sql語句映射生成最終執行的sql,最後將sql執行的結果再映射生成java對象。
Mybatis學習門檻低,簡單易學,程序員直接編寫原生態sql,可嚴格控制sql執行性能,靈活度高,很是適合對關係數據模型要求不高的軟件開發,例如互聯網軟件、企業運營類軟件等,由於這類軟件需求變化頻繁,一但需求變化要求成果輸出迅速。可是靈活的前提是mybatis沒法作到數據庫無關性,若是須要實現支持多種數據庫的軟件則須要自定義多套sql映射文件,工做量大。
Hibernate對象/關係映射能力強,數據庫無關性好,對於關係模型要求高的軟件(例如需求固定的定製化軟件)若是用hibernate開發能夠節省不少代碼,提升效率。可是Hibernate的學習門檻高,要精通門檻更高,並且怎麼設計O/R映射,在性能和對象模型之間如何權衡,以及怎樣用好Hibernate須要具備很強的經驗和能力才行。
總之,按照用戶的需求在有限的資源環境下只要能作出維護性、擴展性良好的軟件架構都是好架構,因此框架只有適合纔是最好。
使用Mybatis開發Dao,一般有兩個方法,即原始Dao開發方法和Mapper接口開發方法。
將下邊的功能實現Dao:
根據用戶id查詢一個用戶信息
根據用戶名稱模糊查詢用戶信息列表
添加用戶信息
SqlSession中封裝了對數據庫的操做,如:查詢、插入、更新、刪除等。
經過SqlSessionFactory建立SqlSession,而SqlSessionFactory是經過SqlSessionFactoryBuilder進行建立。
SqlSessionFactoryBuilder用於建立SqlSessionFacoty,SqlSessionFacoty一旦建立完成就不須要SqlSessionFactoryBuilder了,由於SqlSession是經過SqlSessionFactory生產,因此能夠將SqlSessionFactoryBuilder當成一個工具類使用,最佳使用範圍是方法範圍即方法體內局部變量。
SqlSessionFactory是一個接口,接口中定義了openSession的不一樣重載方法,SqlSessionFactory的最佳使用範圍是整個應用運行期間,一旦建立後能夠重複使用,一般以單例模式管理SqlSessionFactory。
SqlSession是一個面向用戶的接口, sqlSession中定義了數據庫操做,默認使用DefaultSqlSession實現類。
執行過程以下:
Environment environment = configuration.getEnvironment();
if (ExecutorType.BATCH == executorType) { executor = newBatchExecutor(this, transaction); } elseif (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor, autoCommit); }
結論:
每一個線程都應該有它本身的SqlSession實例。SqlSession的實例不能共享使用,它也是線程不安全的。所以最佳的範圍是請求或方法範圍。絕對不能將SqlSession實例的引用放在一個類的靜態字段或實例字段中。
打開一個 SqlSession;使用完畢就要關閉它。一般把這個關閉操做放到 finally塊中以確保每次都能執行關閉。以下:
SqlSession session = sqlSessionFactory.openSession(); try { // do work } finally { session.close(); }
原始Dao開發方法須要程序員編寫Dao接口和Dao實現類。
<?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"> <!-- 根據id獲取用戶信息 --> <select id="findUserById" parameterType="int"resultType="cn.itcast.mybatis.po.User"> select * from user where id = #{id} </select> <!-- 添加用戶 --> <insert id="insertUser"parameterType="cn.itcast.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> </mapper>
Public interface UserDao { public User getUserById(int id) throws Exception; public void insertUser(User user) throws Exception; } Public class UserDaoImpl implements UserDao { //注入SqlSessionFactory public UserDaoImpl(SqlSessionFactory sqlSessionFactory){ this.setSqlSessionFactory(sqlSessionFactory); } private SqlSessionFactory sqlSessionFactory; @Override public User getUserById(int id) throws Exception { SqlSession session = sqlSessionFactory.openSession(); User user = null; try { //經過sqlsession調用selectOne方法獲取一條結果集 //參數1:指定定義的statement的id,參數2:指定向statement中傳遞的參數 user = session.selectOne("test.findUserById", 1); System.out.println(user); } finally{ session.close(); } return user; } @Override Public void insertUser(User user) throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); try { sqlSession.insert("insertUser", user); sqlSession.commit(); } finally{ session.close(); } } }
原始Dao開發中存在如下問題:
Mapper接口開發方法只須要程序員編寫Mapper接口(至關於Dao接口),由Mybatis框架根據接口定義建立接口的動態代理對象,代理對象的方法體同上邊Dao接口實現類方法。
Mapper接口開發須要遵循如下規範:
定義mapper映射文件UserMapper.xml(內容同Users.xml),須要修改namespace的值爲 UserMapper接口路徑。將UserMapper.xml放在classpath下mapper目錄 下。
<?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="cn.itcast.mybatis.mapper.UserMapper"> <!-- 根據id獲取用戶信息 --> <select id="findUserById" parameterType="int"resultType="cn.itcast.mybatis.po.User"> select * from user where id = #{id} </select> <!-- 自定義條件查詢用戶列表 --> <select id="findUserByUsername" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User"> select * from user where username like '%${value}%' </select> <!-- 添加用戶 --> <insert id="insertUser"parameterType="cn.itcast.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> </mapper>
/** * 用戶管理mapper */ Public interface UserMapper { //根據用戶id查詢用戶信息 public User findUserById(int id) throws Exception; //查詢用戶列表 public List<User> findUserByUsername(String username) throwsException; //添加用戶信息 public void insertUser(User user)throws Exception; }
接口定義有以下特色:
修改SqlMapConfig.xml文件:
<!-- 加載映射文件 --> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers>
Public class UserMapperTest extends TestCase { private SqlSessionFactory sqlSessionFactory; protected void setUp() throws Exception { //mybatis配置文件 String resource = "sqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //使用SqlSessionFactoryBuilder建立sessionFactory sqlSessionFactory = newSqlSessionFactoryBuilder().build(inputStream); } Public void testFindUserById() throws Exception { //獲取session SqlSession session = sqlSessionFactory.openSession(); //獲取mapper接口的代理對象 UserMapper userMapper = session.getMapper(UserMapper.class); //調用代理對象方法 User user = userMapper.findUserById(1); System.out.println(user); //關閉session session.close(); } @Test public void testFindUserByUsername() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> list = userMapper.findUserByUsername("張"); System.out.println(list.size()); } Public void testInsertUser() throws Exception { //獲取session SqlSession session = sqlSessionFactory.openSession(); //獲取mapper接口的代理對象 UserMapper userMapper = session.getMapper(UserMapper.class); //要添加的數據 User user = new User(); user.setUsername("張三"); user.setBirthday(new Date()); user.setSex("1"); user.setAddress("北京市"); //經過mapper接口添加用戶 userMapper.insertUser(user); //提交 session.commit(); //關閉session session.close(); } }
動態代理對象調用sqlSession.selectOne()和sqlSession.selectList()是根據mapper接口方法的返回值決定,若是返回list則調用selectList方法,若是返回單個對象則調用selectOne方法。
mybatis官方推薦使用mapper代理方法開發mapper接口,程序員不用編寫mapper接口實現類,使用mapper代理方法時,輸入參數可使用pojo包裝對象或map對象,保證dao的通用性。
SqlMapConfig.xml中配置的內容和順序以下:
properties(屬性)
settings(全局配置參數)
typeAliases(類型別名)
typeHandlers(類型處理器)
objectFactory(對象工廠)
plugins(插件)
environments(環境集合屬性對象)
environment(環境子屬性對象)
transactionManager(事務管理)
dataSource(數據源)
mappers(映射器)
SqlMapConfig.xml能夠引用java屬性文件中的配置信息以下:
在classpath下定義db.properties文件,
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=mysql
SqlMapConfig.xml引用以下:
<properties resource="db.properties"/> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <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>
注意: MyBatis 將按照下面的順序來加載屬性:
所以,經過parameterType傳遞的屬性具備最高優先級,resource或 url 加載的屬性次之,最低優先級的是 properties 元素體內定義的屬性。
mybatis全局配置參數,全局參數將會影響mybatis的運行行爲。
詳細參見「學習資料/mybatis-settings.xlsx」文件
別名 |
映射的類型 |
_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 |
在SqlMapConfig.xml中配置:
<typeAliases> <!-- 單個別名定義 --> <typeAlias alias="user" type="cn.itcast.mybatis.po.User"/> <!-- 批量別名定義,掃描整個包下的類,別名爲類名(首字母大寫或小寫均可以) --> <package name="cn.itcast.mybatis.po"/> <package name="其它包"/> </typeAliases>
類型處理器用於java類型和jdbc類型映射,以下:
<select id="findUserById" parameterType="int" resultType="user"> select * from user where id = #{id} </select>
mybatis自帶的類型處理器基本上知足平常需求,不須要單獨定義。
mybatis支持類型處理器:
類型處理器 |
Java類型 |
JDBC類型 |
BooleanTypeHandler |
Boolean,boolean |
任何兼容的布爾值 |
ByteTypeHandler |
Byte,byte |
任何兼容的數字或字節類型 |
ShortTypeHandler |
Short,short |
任何兼容的數字或短整型 |
IntegerTypeHandler |
Integer,int |
任何兼容的數字和整型 |
LongTypeHandler |
Long,long |
任何兼容的數字或長整型 |
FloatTypeHandler |
Float,float |
任何兼容的數字或單精度浮點型 |
DoubleTypeHandler |
Double,double |
任何兼容的數字或雙精度浮點型 |
BigDecimalTypeHandler |
BigDecimal |
任何兼容的數字或十進制小數類型 |
StringTypeHandler |
String |
CHAR和VARCHAR類型 |
ClobTypeHandler |
String |
CLOB和LONGVARCHAR類型 |
NStringTypeHandler |
String |
NVARCHAR和NCHAR類型 |
NClobTypeHandler |
String |
NCLOB類型 |
ByteArrayTypeHandler |
byte[] |
任何兼容的字節流類型 |
BlobTypeHandler |
byte[] |
BLOB和LONGVARBINARY類型 |
DateTypeHandler |
Date(java.util) |
TIMESTAMP類型 |
DateOnlyTypeHandler |
Date(java.util) |
DATE類型 |
TimeOnlyTypeHandler |
Date(java.util) |
TIME類型 |
SqlTimestampTypeHandler |
Timestamp(java.sql) |
TIMESTAMP類型 |
SqlDateTypeHandler |
Date(java.sql) |
DATE類型 |
SqlTimeTypeHandler |
Time(java.sql) |
TIME類型 |
ObjectTypeHandler |
任意 |
其餘或未指定類型 |
EnumTypeHandler |
Enumeration類型 |
VARCHAR-任何兼容的字符串類型,做爲代碼存儲(而不是索引)。 |
Mapper配置的幾種方法:
使用相對於類路徑的資源
如:<mapper resource="sqlmap/User.xml" />
使用徹底限定路徑
如:<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />
使用mapper接口類路徑
如:<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>
注意:此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。
註冊指定包下的全部mapper接口
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。
Mapper.xml映射文件中定義了操做數據庫的sql,每一個sql是一個statement,映射文件是mybatis的核心。
#{}實現的是向prepareStatement中的預處理語句中設置參數值,sql語句中#{}表示一個佔位符即?。
<!-- 根據id查詢用戶信息 --> <select id="findUserById" parameterType="int" resultType="user"> select * from user where id = #{id} </select>
使用佔位符#{}能夠有效防止sql注入,在使用時不須要關心參數值的類型,mybatis會自動進行java類型和jdbc類型的轉換。#{}能夠接收簡單類型值或pojo屬性值,若是parameterType傳輸單個簡單類型值,#{}括號中能夠是value或其它名稱。
${}和#{}不一樣,經過${}能夠將parameterType 傳入的內容拼接在sql中且不進行jdbc類型轉換, ${}能夠接收簡單類型值或pojo屬性值,若是parameterType傳輸單個簡單類型值,${}括號中只能是value。使用${}不能防止sql注入,可是有時用${}會很是方便,以下的例子:
<!-- 根據名稱模糊查詢用戶信息 --> <select id="selectUserByName" parameterType="string"resultType="user"> select * from user where username like '%${value}%' </select>
若是本例子使用#{}則傳入的字符串中必須有%號,而%是人爲拼接在參數中,顯然有點麻煩,若是採用${}在sql中拼接爲%的方式則在調用mapper接口傳遞參數就方便不少。
//若是使用佔位符號則必須人爲在傳參數中加%
List<User> list = userMapper.selectUserByName("%管理員%");
//若是使用${}原始符號則不用人爲在參數中加%
List<User>list = userMapper.selectUserByName("管理員");
再好比order by排序,若是將列名經過參數傳入sql,根據傳的列名進行排序,應該寫爲:
ORDER BY ${columnName}
若是使用#{}將沒法實現此功能。
參考上邊的例子。
Mybatis使用ognl表達式解析對象字段的值,以下例子:
<!—傳遞pojo對象綜合查詢用戶信息 --> <select id="findUserByUser" parameterType="user"resultType="user"> select * from user where id=#{id} and username like '%${username}%' </select>
上邊紅色標註的是user對象中的字段名稱。
測試:
Public void testFindUserByUser()throws Exception{ //獲取session SqlSession session = sqlSessionFactory.openSession(); //獲限mapper接口實例 UserMapper userMapper = session.getMapper(UserMapper.class); //構造查詢條件user對象 User user = new User(); user.setId(1); user.setUsername("管理員"); //傳遞user對象查詢用戶列表 List<User>list = userMapper.findUserByUser(user); //關閉session session.close(); }
異常測試:
Sql中字段名輸入錯誤後測試,username輸入dusername測試結果報錯:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'dusername' in 'class cn.itcast.mybatis.po.User'
### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'dusername' in 'class cn.itcast.mybatis.po.User'
開發中經過pojo傳遞查詢條件 ,查詢條件是綜合的查詢條件,不只包括用戶查詢條件還包括其它的查詢條件(好比將用戶購買商品信息也做爲查詢條件),這時可使用包裝對象傳遞輸入參數。
定義包裝對象將查詢條件(pojo)以類組合的方式包裝起來。
public class QueryVo { private User user; //自定義用戶擴展類 private UserCustom userCustom;
說明:mybatis底層經過ognl從pojo中獲取屬性值:#{user.username},user便是傳入的包裝對象的屬性。queryVo是別名,即上邊定義的包裝對象類型。
Sql映射文件定義以下:
<!-- 傳遞hashmap綜合查詢用戶信息 --> <select id="findUserByHashmap" parameterType="hashmap"resultType="user"> select * from user where id=#{id} and username like '%${username}%' </select>
上邊紅色標註的是hashmap的key。
測試:
Public void testFindUserByHashmap()throws Exception{ //獲取session SqlSession session = sqlSessionFactory.openSession(); //獲限mapper接口實例 UserMapper userMapper = session.getMapper(UserMapper.class); //構造查詢條件Hashmap對象 HashMap<String, Object> map = new HashMap<String, Object>(); map.put("id", 1); map.put("username", "管理員"); //傳遞Hashmap對象查詢用戶列表 List<User>list = userMapper.findUserByHashmap(map); //關閉session session.close(); }
異常測試:
傳遞的map中的key和sql中解析的key不一致。
測試結果沒有報錯,只是經過key獲取值爲空。
參考getnow輸出日期類型,看下邊的例子輸出整型:
Mapper.xml文件
<!-- 獲取用戶列表總數 --> <select id="findUserCount" parameterType="user"resultType="int"> select count(1) from user </select>
Mapper接口
public int findUserCount(User user) throws Exception;
調用:
總結:
輸出簡單類型必須查詢出來的結果集有一條記錄,最終將第一個字段的值轉換爲輸出類型。
使用session的selectOne可查詢單條記錄。
Public void testFindUserCount() throws Exception{ //獲取session SqlSession session = sqlSessionFactory.openSession(); //獲取mapper接口實例 UserMapper userMapper = session.getMapper(UserMapper.class); User user = new User(); user.setUsername("管理員"); //傳遞Hashmap對象查詢用戶列表 int count = userMapper.findUserCount(user); //關閉session session.close(); }
參考findUserById的定義:
Mapper.xml
<!-- 根據id查詢用戶信息 -->
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
Mapper接口:
public User findUserById(int id) throws Exception;
測試:
Public void testFindUserById() throws Exception {
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper接口實例
UserMapper userMapper = session.getMapper(UserMapper.class);
//經過mapper接口調用statement
User user = userMapper.findUserById(1);
System.out.println(user);
//關閉session
session.close();
}
使用session調用selectOne查詢單條記錄。
參考selectUserByName的定義:
Mapper.xml
<!-- 根據名稱模糊查詢用戶信息 -->
<select id="findUserByUsername" parameterType="string"resultType="user">
select * from user where username like '%${value}%'
</select>
Mapper接口:
public List<User> findUserByUsername(String username) throws Exception;
測試:
Public void testFindUserByUsername()throws Exception{
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper接口實例
UserMapper userMapper = session.getMapper(UserMapper.class);
//若是使用佔位符號則必須人爲在傳參數中加%
//List<User> list = userMapper.selectUserByName("%管理員%");
//若是使用${}原始符號則不用人爲在參數中加%
List<User> list = userMapper.findUserByUsername("管理員");
//關閉session
session.close();
}
使用session的selectList方法獲取pojo列表。
輸出pojo對象和輸出pojo列表在sql中定義的resultType是同樣的。
返回單個pojo對象要保證sql查詢出來的結果集爲單條,內部使用session.selectOne方法調用,mapper接口使用pojo對象做爲方法返回值。
返回pojo列表表示查詢出來的結果集可能爲多條,內部使用session.selectList方法,mapper接口使用List<pojo>對象做爲方法返回值。
輸出pojo對象能夠改用hashmap輸出類型,將輸出的字段名稱做爲map的key,value爲字段值。
resultType能夠指定pojo將查詢結果映射爲pojo,但須要pojo的屬性名和sql查詢的列名一致方可映射成功。
若是sql查詢字段名和pojo的屬性名不一致,能夠經過resultMap將字段名和屬性名做一個對應關係 ,resultMap實質上還須要將查詢結果映射到pojo對象中。
resultMap能夠實現將查詢結果映射爲複雜類型的pojo,好比在查詢結果映射對象中包括pojo和list實現一對一查詢和一對多查詢。
使用resultMap指定上邊定義的personmap。
因爲上邊的mapper.xml中sql查詢列和Users.java類屬性不一致,須要定義resultMap:userListResultMap將sql查詢列和Users.java類屬性對應起來
<id />:此屬性表示查詢結果集的惟一標識,很是重要。若是是多個字段爲複合惟一約束則定義多個<id />。
Property:表示person類的屬性。
Column:表示sql查詢出來的字段名。
Column和property放在一起表示將sql查詢出來的字段映射到指定的pojo類屬性上。
<result />:普通結果,即pojo的屬性。
public List<User> findUserListResultMap() throws Exception;
經過mybatis提供的各類標籤方法實現動態拼接sql。
<!-- 傳遞pojo綜合查詢用戶信息 -->
<select id="findUserList" parameterType="user"resultType="user">
select * from user
where 1=1
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</select>
注意要作不等於空字符串校驗。
上邊的sql也能夠改成:
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
<where />能夠自動處理第一個and。
向sql傳遞數組或List,mybatis使用foreach解析,以下:
傳入多個id查詢用戶信息,用下邊兩個sql實現:
SELECT * FROM USERS WHERE username LIKE '%張%' AND (id =10 OR id =89 OR id=16)
SELECT * FROM USERS WHERE username LIKE '%張%' id IN (10,89,16)
<if test="ids!=null and ids.size>0">
<foreach collection="ids" open=" and id in(" close=")"item="id" separator="," >
#{id}
</foreach>
</if>
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);//查詢id爲1的用戶
ids.add(10); //查詢id爲10的用戶
queryVo.setIds(ids);
List<User> list = userMapper.findUserList(queryVo);
傳遞List類型在編寫mapper.xml沒有區別,惟一不一樣的是隻有一個List參數時它的參數名爲list。
以下:
<select id="selectUserByList" parameterType="java.util.List"resultType="user">
select * from user
<where>
<!-- 傳遞List,List中是pojo -->
<if test="list!=null">
<foreach collection="list" item="item" open="and id in("separator=","close=")">
#{item.id}
</foreach>
</if>
</where>
</select>
public List<User> selectUserByList(List userlist) throws Exception;
Public void testselectUserByList()throws Exception{
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper接口實例
UserMapper userMapper = session.getMapper(UserMapper.class);
//構造查詢條件List
List<User> userlist = new ArrayList<User>();
User user = new User();
user.setId(1);
userlist.add(user);
user = new User();
user.setId(2);
userlist.add(user);
//傳遞userlist列表查詢用戶列表
List<User>list = userMapper.selectUserByList(userlist);
//關閉session
session.close();
}
請閱讀文檔學習。
<!-- 傳遞數組綜合查詢用戶信息 -->
<select id="selectUserByArray" parameterType="Object[]"resultType="user">
select * from user
<where>
<!-- 傳遞數組 -->
<if test="array!=null">
<foreach collection="array" index="index" item="item"open="and id in("separator=","close=")">
#{item.id}
</foreach>
</if>
</where>
</select>
sql只接收一個數組參數,這時sql解析參數的名稱mybatis固定爲array,若是數組是經過一個pojo傳遞到sql則參數的名稱爲pojo中的屬性名。
index:爲數組的下標。
item:爲數組每一個元素的名稱,名稱隨意定義
open:循環開始
close:循環結束
separator:中間分隔輸出
public List<User> selectUserByArray(Object[] userlist) throws Exception;
Public void testselectUserByArray()throws Exception{
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper接口實例
UserMapper userMapper = session.getMapper(UserMapper.class);
//構造查詢條件List
Object[] userlist = new Object[2];
User user = new User();
user.setId(1);
userlist[0]=user;
user = new User();
user.setId(2);
userlist[1]=user;
//傳遞user對象查詢用戶列表
List<User>list = userMapper.selectUserByArray(userlist);
//關閉session
session.close();
}
請閱讀文檔學習。
<!-- 傳遞數組綜合查詢用戶信息 -->
<select id="selectUserByArray" parameterType="Object[]"resultType="user">
select * from user
<where>
<!-- 傳遞數組 -->
<if test="array!=null">
<foreach collection="array"index="index"item="item"open="and id in("separator=","close=")">
#{item}
</foreach>
</if>
</where>
</select>
若是數組中是簡單類型則寫爲#{item},不用再經過ognl獲取對象屬性值了。
public List<User> selectUserByArray(Object[] userlist) throws Exception;
Public void testselectUserByArray()throws Exception{
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper接口實例
UserMapper userMapper = session.getMapper(UserMapper.class);
//構造查詢條件List
Object[] userlist = new Object[2];
userlist[0]=」1」;
userlist[1]=」2」;
//傳遞user對象查詢用戶列表
List<User>list = userMapper.selectUserByArray(userlist);
//關閉session
session.close();
}
Sql中可將重複的sql提取出來,使用時用include引用便可,最終達到sql重用的目的,以下:
<!-- 傳遞pojo綜合查詢用戶信息 -->
<select id="findUserList" parameterType="user"resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
<sql id="query_user_where">
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</sql>
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<include refid="query_user_where"/>
</where>
</select>
注意:若是引用其它mapper.xml的sql片斷,則在引用時須要加上namespace,以下:
<include refid="namespace.sql片斷」/>
案例:查詢全部訂單信息,關聯查詢下單用戶信息。
注意:由於一個訂單信息只會是一我的下的訂單,因此從查詢訂單信息出發關聯查詢用戶信息爲一對一查詢。若是從用戶信息出發查詢用戶下的訂單信息則爲一對多查詢,由於一個用戶能夠下多個訂單。
使用resultType,定義訂單信息po類,此po類中包括了訂單信息和用戶信息:
SELECT
orders.*,
user.username,
userss.address
FROM
orders,
user
WHERE orders.user_id = user.id
Po類中應該包括上邊sql查詢出來的全部字段,以下:
public class OrdersCustom extends Orders {
private String username;// 用戶名稱
private String address;// 用戶地址
get/set。。。。
OrdersCustom類繼承Orders類後OrdersCustom類包括了Orders類的全部字段,只須要定義用戶的信息字段便可。
<!-- 查詢全部訂單信息 -->
<select id="findOrdersList"resultType="cn.itcast.mybatis.po.OrdersCustom">
SELECT
orders.*,
user.username,
user.address
FROM
orders, user
WHERE orders.user_id = user.id
</select>
public List<OrdersCustom> findOrdersList() throws Exception;
Public void testfindOrdersList()throws Exception{
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper接口實例
UserMapper userMapper = session.getMapper(UserMapper.class);
//查詢訂單信息
List<OrdersCustom> list = userMapper.findOrdersList();
System.out.println(list);
//關閉session
session.close();
}
定義專門的po類做爲輸出類型,其中定義了sql查詢結果集全部的字段。此方法較爲簡單,企業中使用廣泛。
使用resultMap,定義專門的resultMap用於映射一對一查詢結果。
SELECT
orders.*,
user.username,
user.address
FROM
orders,
user
WHERE orders.user_id = user.id
在Orders類中加入User屬性,user屬性中用於存儲關聯查詢的用戶信息,由於訂單關聯查詢用戶是一對一關係,因此這裏使用單個User對象存儲關聯查詢的用戶信息。
<select id="findOrdersListResultMap" resultMap="userordermap">
SELECT
orders.*,
user.username,
user.address
FROM
orders, user
WHERE orders.user_id = user.id
</select>
這裏resultMap指定userordermap。
須要關聯查詢映射的是用戶信息,使用association將用戶信息映射到訂單對象的用戶屬性中。
<!-- 訂單信息resultmap -->
<resultMap type="cn.itcast.mybatis.po.Orders" id="userordermap">
<!-- 這裏的id,是mybatis在進行一對一查詢時將user字段映射爲user對象時要使用,必須寫 -->
<id property="id" column="id"/>
<result property="user_id" column="user_id"/>
<result property="number" column="number"/>
<association property="user" javaType="cn.itcast.mybatis.po.User">
<!-- 這裏的id爲user的id,若是寫上表示給user的id屬性賦值 -->
<id property="id" column="user_id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
</association>
</resultMap>
association:表示進行關聯查詢單條記錄
property:表示關聯查詢的結果存儲在cn.itcast.mybatis.po.Orders的user屬性中
javaType:表示關聯查詢的結果類型
<id property="id" column="user_id"/>:查詢結果的user_id列對應關聯對象的id屬性,這裏是<id />表示user_id是關聯查詢對象的惟一標識。
<result property="username" column="username"/>:查詢結果的username列對應關聯對象的username屬性。
public List<Orders> findOrdersListResultMap() throws Exception;
Public void testfindOrdersListResultMap()throws Exception{
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper接口實例
UserMapper userMapper = session.getMapper(UserMapper.class);
//查詢訂單信息
List<Orders> list = userMapper.findOrdersList2();
System.out.println(list);
//關閉session
session.close();
}
使用association完成關聯查詢,將關聯查詢信息映射到pojo對象中。
案例:查詢全部訂單信息及訂單下的訂單明細信息。
訂單信息與訂單明細爲一對多關係。
使用resultMap實現以下:
SELECT
orders.*,
user.username,
user.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num
FROM
orders,user,orderdetail
WHERE orders.user_id = user.id
AND orders.id = orderdetail.orders_id
在Orders類中加入User屬性。
在Orders類中加入List<Orderdetail> orderdetails屬性
<select id="findOrdersDetailList" resultMap="userorderdetailmap">
SELECT
orders.*,
user.username,
user.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num
FROM orders,user,orderdetail
WHERE orders.user_id = user.id
AND orders.id = orderdetail.orders_id
</select>
<!-- 訂單信息resultmap -->
<resultMap type="cn.itcast.mybatis.po.Orders"id="userorderdetailmap">
<id property="id"column="id"/>
<result property="user_id" column="user_id"/>
<result property="number" column="number"/>
<association property="user" javaType="cn.itcast.mybatis.po.User">
<id property="id" column="user_id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
</association>
<collection property="orderdetails"ofType="cn.itcast.mybatis.po.Orderdetail">
<id property="id" column="orderdetail_id"/>
<result property="items_id" column="items_id"/>
<result property="items_num" column="items_num"/>
</collection>
</resultMap>
黃色部分和上邊一對一查詢訂單及用戶信息定義的resultMap相同,
collection部分定義了查詢訂單明細信息。
collection:表示關聯查詢結果集
property="orderdetails":關聯查詢的結果集存儲在cn.itcast.mybatis.po.Orders上哪一個屬性。
ofType="cn.itcast.mybatis.po.Orderdetail":指定關聯查詢的結果集中的對象類型即List中的對象類型。
<id />及<result/>的意義同一對一查詢。
上邊定義的resultMap中黃色部分和一對一查詢訂單信息的resultMap相同,這裏使用繼承能夠再也不填寫重複的內容,以下:
<resultMap type="cn.itcast.mybatis.po.Orders"id="userorderdetailmap" extends="userordermap">
<collection property="orderdetails"ofType="cn.itcast.mybatis.po.Orderdetail">
<id property="id" column="orderdetail_id"/>
<result property="items_id" column="items_id"/>
<result property="items_num" column="items_num"/>
</collection>
</resultMap>
使用extends繼承訂單信息userordermap。
public List<Orders>findOrdersDetailList () throws Exception;
Public void testfindOrdersDetailList()throws Exception{
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper接口實例
UserMapper userMapper = session.getMapper(UserMapper.class);
//查詢訂單信息
List<Orders> list = userMapper.findOrdersDetailList();
System.out.println(list);
//關閉session
session.close();
}
使用collection完成關聯查詢,將關聯查詢信息映射到集合對象。
查詢用戶購買的商品信息。
須要查詢全部用戶信息,關聯查詢訂單及訂單明細信息,訂單明細信息中關聯查詢商品信息
SELECT
orders.*,
USER .username,
USER .address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
items.name items_name,
items.detail items_detail
FROM
orders,
USER,
orderdetail,
items
WHERE
orders.user_id = USER .id
AND orders.id = orderdetail.orders_id
AND orderdetail.items_id = items.id
在User中添加List<Orders> orders 屬性,在Orders類中加入List<Orderdetail> orderdetails屬性
須要關聯查詢映射的信息是:訂單、訂單明細、商品信息
訂單:一個用戶對應多個訂單,使用collection映射到用戶對象的訂單列表屬性中
訂單明細:一個訂單對應多個明細,使用collection映射到訂單對象中的明細屬性中
商品信息:一個訂單明細對應一個商品,使用association映射到訂單明細對象的商品屬性中。
<!-- 一對多查詢
查詢用戶信息、關聯查詢訂單、訂單明細信息、商品信息
-->
<resultMap type="cn.itcast.mybatis.po.User"id="userOrderListResultMap">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<collection property="orders"ofType="cn.itcast.mybatis.po.Orders">
<id column="id" property="id"/>
<result property="number" column="number"/>
<collection property="orderdetails"ofType="cn.itcast.mybatis.po.Orderdetail">
<id column="orderdetail_id" property="id"/>
<result property="ordersId" column="id"/>
<result property="itemsId" column="items_id"/>
<result property="itemsNum" column="items_num"/>
<association property="items"javaType="cn.itcast.mybatis.po.Items">
<id column="items_id" property="id"/>
<result column="items_name" property="name"/>
<result column="items_detail" property="detail"/>
</association>
</collection>
</collection>
</resultMap>
一對可能是多對多的特例,以下需求:
查詢用戶購買的商品信息,用戶和商品的關係是多對多關係。
需求1:
查詢字段:用戶帳號、用戶名稱、用戶性別、商品名稱、商品價格(最多見)
企業開發中常見明細列表,用戶購買商品明細列表,
使用resultType將上邊查詢列映射到pojo輸出。
需求2:
查詢字段:用戶帳號、用戶名稱、購買商品數量、商品明細(鼠標移上顯示明細)
使用resultMap將用戶購買的商品明細列表映射到user對象中。
resultType:
做用:
將查詢結果按照sql列名pojo屬性名一致性映射到pojo中。
場合:
常見一些明細記錄的展現,好比用戶購買商品明細,將關聯查詢信息所有展現在頁面時,此時可直接使用resultType將每一條記錄映射到pojo中,在前端頁面遍歷list(list中是pojo)便可。
resultMap:
使用association和collection完成一對一和一對多高級映射(對結果有特殊的映射要求)。
association:
做用:
將關聯查詢信息映射到一個pojo對象中。
場合:
爲了方便查詢關聯信息可使用association將關聯訂單信息映射爲用戶對象的pojo屬性中,好比:查詢訂單及關聯用戶信息。
使用resultType沒法將查詢結果映射到pojo對象的pojo屬性中,根據對結果集查詢遍歷的須要選擇使用resultType仍是resultMap。
collection:
做用:
將關聯查詢信息映射到一個list集合中。
場合:
爲了方便查詢遍歷關聯信息可使用collection將關聯信息映射到list集合中,好比:查詢用戶權限範圍模塊及模塊下的菜單,可以使用collection將模塊映射到模塊list中,將菜單列表映射到模塊對象的菜單list屬性中,這樣的做的目的也是方便對查詢結果集進行遍歷查詢。
若是使用resultType沒法將查詢結果映射到list集合中。
須要查詢關聯信息時,使用mybatis延遲加載特性可有效的減小數據庫壓力,首次查詢只查詢主要信息,關聯信息等用戶獲取時再加載。
在mybatis核心配置文件中配置:
lazyLoadingEnabled、aggressiveLazyLoading
設置項 |
描述 |
容許值 |
默認值 |
lazyLoadingEnabled |
全局性設置懶加載。若是設爲‘false’,則全部相關聯的都會被初始化加載。 |
true | false |
false |
aggressiveLazyLoading |
當設置爲‘true’的時候,懶加載的對象可能被任何懶屬性所有加載。不然,每一個屬性都按需加載。 |
true | false |
true |
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
查詢訂單信息,關聯查詢用戶信息。
默認只查詢訂單信息,當須要查詢用戶信息時再去查詢用戶信息。
SELECT
orders.*
FROM
orders
在Orders類中加入User屬性。
<select id="findOrdersList3" resultMap="userordermap2">
SELECT
orders.*
FROM
orders
</select>
<!-- 訂單信息resultmap -->
<resultMap type="cn.itcast.mybatis.po.Orders" id="userordermap2">
<id property="id" column="id"/>
<result property="user_id" column="user_id"/>
<result property="number" column="number"/>
<association property="user" javaType="cn.itcast.mybatis.po.User"select="findUserById" column="user_id"/>
</resultMap>
association:
select="findUserById":指定關聯查詢sql爲findUserById
column="user_id":關聯查詢時將users_id列的值傳入findUserById
最後將關聯查詢結果映射至cn.itcast.mybatis.po.User。
public List<Orders> findOrdersList3() throws Exception;
Public void testfindOrdersList3()throws Exception{
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper接口實例
UserMapper userMapper = session.getMapper(UserMapper.class);
//查詢訂單信息
List<Orders> list = userMapper.findOrdersList3();
System.out.println(list);
//開始加載,經過orders.getUser方法進行加載
for(Orders orders:list){
System.out.println(orders.getUser());
}
//關閉session
session.close();
}
不使用mybatis提供的延遲加載功能是否能夠實現延遲加載?
實現方法:
針對訂單和用戶兩個表定義兩個mapper方法。
一、訂單查詢mapper方法
二、根據用戶id查詢用戶信息mapper方法
默認使用訂單查詢mapper方法只查詢訂單信息。
當須要關聯查詢用戶信息時再調用根據用戶id查詢用戶信息mapper方法查詢用戶信息。
一對多延遲加載的方法同一對一延遲加載,在collection標籤中配置select內容。
本部份內容自學。
做用:
當須要查詢關聯信息時再去數據庫查詢,默認不去關聯查詢,提升數據庫性能。
只有使用resultMap支持延遲加載設置。
場合:
當只有部分記錄須要關聯查詢其它信息時,此時可按需延遲加載,須要關聯查詢時再向數據庫發出sql,以提升數據庫性能。
當所有須要關聯查詢信息時,此時不用延遲加載,直接將關聯查詢信息所有返回便可,可以使用resultType或resultMap完成映射。
以下圖,是mybatis一級緩存和二級緩存的區別圖解:
Mybatis一級緩存的做用域是同一個SqlSession,在同一個sqlSession中兩次執行相同的sql語句,第一次執行完畢會將數據庫中查詢的數據寫到緩存(內存),第二次會從緩存中獲取數據將再也不從數據庫查詢,從而提升查詢效率。當一個sqlSession結束後該sqlSession中的一級緩存也就不存在了。Mybatis默認開啓一級緩存。
Mybatis二級緩存是多個SqlSession共享的,其做用域是mapper的同一個namespace,不一樣的sqlSession兩次執行相同namespace下的sql語句且向sql中傳遞參數也相同即最終執行相同的sql語句,第一次執行完畢會將數據庫中查詢的數據寫到緩存(內存),第二次會從緩存中獲取數據將再也不從數據庫查詢,從而提升查詢效率。Mybatis默認沒有開啓二級緩存須要在setting全局參數中配置開啓二級緩存。
下圖是根據id查詢用戶的一級緩存圖解:
一級緩存區域是根據SqlSession爲單位劃分的。
每次查詢會先從緩存區域找,若是找不到從數據庫查詢,查詢到數據將數據寫入緩存。
Mybatis內部存儲緩存使用一個HashMap,key爲hashCode+sqlId+Sql語句。value爲從查詢出來映射生成的java對象
sqlSession執行insert、update、delete等操做commit提交後會清空緩存區域。
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper接口實例
UserMapper userMapper = session.getMapper(UserMapper.class);
//第一次查詢
User user1 = userMapper.findUserById(1);
System.out.println(user1);
//第二次查詢,因爲是同一個session則再也不向數據發出語句直接從緩存取出
User user2 = userMapper.findUserById(1);
System.out.println(user2);
//關閉session
session.close();
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper接口實例
UserMapper userMapper = session.getMapper(UserMapper.class);
//第一次查詢
User user1 = userMapper.findUserById(1);
System.out.println(user1);
//在同一個session執行更新
User user_update = new User();
user_update.setId(1);
user_update.setUsername("李奎");
userMapper.updateUser(user_update);
session.commit();
//第二次查詢,雖然是同一個session可是因爲執行了更新操做session的緩存被清空,這裏從新發出sql操做
User user2 = userMapper.findUserById(1);
System.out.println(user2);
下圖是多個sqlSession請求UserMapper的二級緩存圖解。
二級緩存區域是根據mapper的namespace劃分的,相同namespace的mapper查詢數據放在同一個區域,若是使用mapper代理方法每一個mapper的namespace都不一樣,此時能夠理解爲二級緩存區域是根據mapper劃分。
每次查詢會先從緩存區域找,若是找不到從數據庫查詢,查詢到數據將數據寫入緩存。
Mybatis內部存儲緩存使用一個HashMap,key爲hashCode+sqlId+Sql語句。value爲從查詢出來映射生成的java對象
sqlSession執行insert、update、delete等操做commit提交後會清空緩存區域。
在覈心配置文件SqlMapConfig.xml中加入
<setting name="cacheEnabled" value="true"/>
|
描述 |
容許值 |
默認值 |
cacheEnabled |
對在此配置文件下的全部cache 進行全局性開/關設置。 |
true false |
true |
要在你的Mapper映射文件中添加一行: <cache /> ,表示此mapper開啓二級緩存。
二級緩存須要查詢結果映射的pojo對象實現java.io.Serializable接口實現序列化和反序列化操做,注意若是存在父類、成員pojo都須要實現序列化接口。
public class Orders implements Serializable
public class User implements Serializable
....
//獲取session1
SqlSession session1 = sqlSessionFactory.openSession();
UserMapper userMapper = session1.getMapper(UserMapper.class);
//使用session1執行第一次查詢
User user1 = userMapper.findUserById(1);
System.out.println(user1);
//關閉session1
session1.close();
//獲取session2
SqlSession session2 = sqlSessionFactory.openSession();
UserMapper userMapper2 = session2.getMapper(UserMapper.class);
//使用session2執行第二次查詢,因爲開啓了二級緩存這裏從緩存中獲取數據再也不向數據庫發出sql
User user2 = userMapper2.findUserById(1);
System.out.println(user2);
//關閉session2
session2.close();
在statement中設置useCache=false能夠禁用當前select語句的二級緩存,即每次查詢都會發出sql去查詢,默認狀況是true,即該sql使用二級緩存。
<select id="findOrderListResultMap" resultMap="ordersUserMap"useCache="false">
在mapper的同一個namespace中,若是有其它insert、update、delete操做數據後須要刷新緩存,若是不執行刷新緩存會出現髒讀。
設置statement配置中的flushCache="true" 屬性,默認狀況下爲true即刷新緩存,若是改爲false則不會刷新。使用緩存時若是手動修改數據庫表中的查詢數據會出現髒讀。
以下:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User"flushCache="true">
flushInterval(刷新間隔)能夠被設置爲任意的正整數,並且它們表明一個合理的毫秒形式的時間段。默認狀況是不設置,也就是沒有刷新間隔,緩存僅僅調用語句時刷新。
size(引用數目)能夠被設置爲任意正整數,要記住你緩存的對象數目和你運行環境的可用內存資源數目。默認值是1024。
readOnly(只讀)屬性能夠被設置爲true或false。只讀的緩存會給全部調用者返回緩存對象的相同實例。所以這些對象不能被修改。這提供了很重要的性能優點。可讀寫的緩存會返回緩存對象的拷貝(經過序列化)。這會慢一些,可是安全,所以默認是false。
以下例子:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
這個更高級的配置建立了一個 FIFO 緩存,並每隔 60 秒刷新,存數結果對象或列表的 512 個引用,並且返回的對象被認爲是隻讀的,所以在不一樣線程中的調用者之間修改它們會致使衝突。可用的收回策略有, 默認的是 LRU:
1. LRU – 最近最少使用的:移除最長時間不被使用的對象。
2. FIFO – 先進先出:按對象進入緩存的順序來移除它們。
3. SOFT – 軟引用:移除基於垃圾回收器狀態和軟引用規則的對象。
4. WEAK – 弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的對象。
EhCache 是一個純Java的進程內緩存框架,是一種普遍使用的開源Java分佈式緩存,具備快速、精幹等特色,是Hibernate中默認的CacheProvider。
mybatis提供二級緩存Cache接口,以下:
它的默認實現類:
經過實現Cache接口能夠實現mybatis緩存數據經過其它緩存數據庫整合,mybatis的特長是sql操做,緩存數據的管理不是mybatis的特長,爲了提升緩存的性能將mybatis和第三方的緩存數據庫整合,好比ehcache、memcache、redis等。
maven座標:
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.2</version>
</dependency>
classpath下添加:ehcache.xml
內容以下:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="F:\develop\ehcache" />
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
屬性說明:
diskStore:指定數據在磁盤中的存儲位置。
defaultCache:當藉助CacheManager.add("demoCache")建立Cache時,EhCache便會採用<defalutCache/>指定的的管理策略
如下屬性是必須的:
maxElementsInMemory - 在內存中緩存的element的最大數目
maxElementsOnDisk - 在磁盤上緩存的element的最大數目,如果0表示無窮大
eternal - 設定緩存的elements是否永遠不過時。若是爲true,則緩存的數據始終有效,若是爲false那麼還要根據timeToIdleSeconds,timeToLiveSeconds判斷
overflowToDisk - 設定當內存緩存溢出的時候是否將過時的element緩存到磁盤上
如下屬性是可選的:
timeToIdleSeconds - 當緩存在EhCache中的數據先後兩次訪問的時間超過timeToIdleSeconds的屬性取值時,這些數據便會刪除,默認值是0,也就是可閒置時間無窮大
timeToLiveSeconds - 緩存element的有效生命期,默認是0.,也就是element存活時間無窮大
diskSpoolBufferSizeMB 這個參數設置DiskStore(磁盤緩存)的緩存區大小.默認是30MB.每一個Cache都應該有本身的一個緩衝區.
diskPersistent - 在VM重啓的時候是否啓用磁盤保存EhCache中的數據,默認是false。
diskExpiryThreadIntervalSeconds - 磁盤緩存的清理線程運行間隔,默認是120秒。每一個120s,相應的線程會進行一次EhCache中數據的清理工做
memoryStoreEvictionPolicy - 當內存緩存達到最大,有新的element加入的時候,移除緩存中element的策略。默認是LRU(最近最少使用),可選的有LFU(最不常使用)和FIFO(先進先出)
EhcacheCache是ehcache對Cache接口的實現:
修改mapper.xml文件,在cache中指定EhcacheCache。
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
根據需求調整緩存參數:
<cache type="org.mybatis.caches.ehcache.EhcacheCache" >
<property name="timeToIdleSeconds" value="3600"/>
<property name="timeToLiveSeconds" value="3600"/>
<!-- 同ehcache參數maxElementsInMemory -->
<property name="maxEntriesLocalHeap" value="1000"/>
<!-- 同ehcache參數maxElementsOnDisk -->
<property name="maxEntriesLocalDisk" value="10000000"/>
<property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>
對於訪問多的查詢請求且用戶對查詢結果實時性要求不高,此時可採用mybatis二級緩存技術下降數據庫訪問量,提升訪問速度,業務場景好比:耗時較高的統計分析sql、電話帳單查詢sql等。
實現方法以下:經過設置刷新間隔時間,由mybatis每隔一段時間自動清空緩存,根據數據變化頻率設置緩存刷新間隔flushInterval,好比設置爲30分鐘、60分鐘、24小時等,根據需求而定。
mybatis二級緩存對細粒度的數據級別的緩存實現很差,好比以下需求:對商品信息進行緩存,因爲商品信息查詢訪問量大,可是要求用戶每次都能查詢最新的商品信息,此時若是使用mybatis的二級緩存就沒法實現當一個商品變化時只刷新該商品的緩存信息而不刷新其它商品的信息,由於mybaits的二級緩存區域以mapper爲單位劃分,當一個商品信息變化會將全部商品信息的緩存數據所有清空。解決此類問題須要在業務層根據需求對數據有針對性緩存。
實現mybatis與spring進行整合,經過spring管理SqlSessionFactory、mapper接口。
mybatis官方提供與mybatis與spring整合jar包:
還包括其它jar:
spring3.2.0
mybatis3.2.7
dbcp鏈接池
數據庫驅動
參考:
在classpath下建立mybatis/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>
<!—使用自動掃描器時,mapper.xml文件若是和mapper.java接口在一個目錄則此處不用定義mappers -->
<mappers>
<package name="cn.itcast.mybatis.mapper" />
</mappers>
</configuration>
在classpath下建立applicationContext.xml,定義數據庫連接池、SqlSessionFactory。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!-- 加載配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 數據庫鏈接池 -->
<bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="10"/>
<property name="maxIdle" value="5"/>
</bean>
<!-- mapper配置 -->
<!-- 讓spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
<bean id="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 數據庫鏈接池 -->
<property name="dataSource" ref="dataSource" />
<!-- 加載mybatis的全局配置文件 -->
<property name="configLocation"value="classpath:mybatis/SqlMapConfig.xml" />
</bean>
</beans>
注意:在定義sqlSessionFactory時指定數據源dataSource和mybatis的配置文件。
使用此種方法即原始dao開發方法,須要編寫dao接口,dao接口實現類、映射文件。
<mappers>
<mapper resource="mapper.xml文件的地址" />
<mapper resource="mapper.xml文件的地址" />
</mappers>
dao接口實現類方法中能夠this.getSqlSession()進行數據增刪改查。
<bean id=" "class="mapper接口的實現">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
此方法即mapper接口開發方法,只需定義mapper接口,不用編寫mapper接口實現類。每一個mapper接口都須要在spring配置文件中定義。
若是mapper.xml和mappre接口的名稱相同且在同一個目錄,這裏能夠不用配置
<mappers>
<mapper resource="mapper.xml文件的地址" />
<mapper resource="mapper.xml文件的地址" />
</mappers>
<bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="mapper接口地址"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
此方法即mapper接口開發方法,只需定義mapper接口,不用編寫mapper接口實現類。只須要在spring配置文件中定義一個mapper掃描器,自動掃描包中的mapper接口生成代代理對象。
注意mapper.xml的文件名和mapper的接口名稱保持一致,且放在同一個目錄
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="mapper接口包地址"></property>
<property name="sqlSessionFactoryBeanName"value="sqlSessionFactory"/>
</bean>
basePackage:掃描包路徑,中間能夠用逗號或分號分隔定義多個包
若是將mapper.xml和mapper接口的名稱保持一致且放在一個目錄 則不用在sqlMapConfig.xml中進行配置
使用官方網站的mapper自動生成工具mybatis-generator-core-1.3.2來生成po類和mapper映射文件。
在generatorConfig.xml中配置mapper生成的詳細信息,注意改下幾點:
配置文件以下:
詳見generatorSqlmapCustom工程
Public void generator() throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = newMyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
Public static void main(String[] args) throws Exception {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
Mapper.xml的文件拷貝至mapper目錄內
Mapper.java的文件拷貝至mapper 目錄內
注意:mapper xml文件和mapper.java文件在一個目錄內且文件名相同。
學會使用mapper自動生成的增、刪、改、查方法。
//刪除符合條件的記錄
int deleteByExample(UserExample example);
//根據主鍵刪除
int deleteByPrimaryKey(String id);
//插入對象全部字段
int insert(User record);
//插入對象不爲空的字段
int insertSelective(User record);
//自定義查詢條件查詢結果集
List<User> selectByExample(UserExample example);
//根據主鍵查詢
UserselectByPrimaryKey(String id);
//根據主鍵將對象中不爲空的值更新至數據庫
int updateByPrimaryKeySelective(User record);
//根據主鍵將對象中全部字段的值更新至數據庫
int updateByPrimaryKey(User record);
XXXMapper.xml文件已經存在時,若是進行從新生成則mapper.xml文件內容不被覆蓋而是進行內容追加,結果致使mybatis解析失敗。
解決方法:刪除原來已經生成的mapper xml文件再進行生成。
Mybatis自動生成的po及mapper.java文件不是內容而是直接覆蓋沒有此問題。
下邊是關於針對oracle數據庫表生成代碼的schema問題:
Schma即數據庫模式,oracle中一個用戶對應一個schema,能夠理解爲用戶就是schema。
當Oralce數據庫存在多個schema能夠訪問相同的表名時,使用mybatis生成該表的mapper.xml將會出現mapper.xml內容重複的問題,結果致使mybatis解析錯誤。
解決方法:在table中填寫schema,以下:
<table schema="XXXX" tableName=" " >
XXXX即爲一個schema的名稱,生成後將mapper.xml的schema前綴批量去掉,若是不去掉當oracle用戶變動了sql語句將查詢失敗。
快捷操做方式:mapper.xml文件中批量替換:「from XXXX.」爲空
Oracle查詢對象的schema可從dba_objects中查詢,以下:
select * from dba_objects