開源頂級持久層框架——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 {