Mybatis應用學習——簡單使用示例

1. 傳統JDBC程序中存在的問題

    1. 一個簡單的JDBC程序示例:java

public class JDBCDemo {
	public static void main(String[] args) {
		Connection con=null;
		PreparedStatement statement=null;
		ResultSet rs=null;
		try {
//首先獲取到與數據庫的鏈接對象
			Properties pro=new Properties();
			pro.load(JDBCDemo3.class.getClassLoader().getResourceAsStream("jdbc/db.properties"));
			String driver=pro.getProperty("driver");
			String user=pro.getProperty("user");
			String url=pro.getProperty("url");
			String password=pro.getProperty("password");
			Class.forName(driver);
			con=DriverManager.getConnection(url, user, password);
//執行sql語句
			String id="1";
			String sql="select * from employee where id=?";
			statement=con.prepareStatement(sql);
			statement.setInt(1,2);
			rs=statement.executeQuery();
			while(rs.next()){
				System.out.println(rs.getInt("id"));
				System.out.println(rs.getString(2));
				System.out.println(rs.getFloat("salary"));
				System.out.println();
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
//關閉資源
			try {
				if(rs!=null){
					rs.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs=null;
			try {
				if(statement!=null){
					statement.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			statement=null;
			try {
				if(con!=null){
					con.close();		
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			con=null;
		}
	}
}

    2. 上面代碼形成的問題:mysql

  • 頻繁的建立和釋放數據庫鏈接對象,極大消耗數據庫性能,解決:能夠經過數據庫鏈接池技術(c3p0、DBCP、druid)來管理數據庫鏈接對象
  • SQL語句硬編碼到Java代碼中,也就是把SQL語句寫死了,若是想要執行其餘SQL語句則必須修改代碼或重寫一份,不利於系統維護。解決辦法就是將SQL語句提取出來,能夠寫在xml文件中,若是須要修改,只須要修改SQL語句便可,不須要修改Java源碼。
  • PreparedStatement 對象中須要設置SQL語句中佔位符處所須要設置的參數,此處存在參數硬編碼,因此和SQL語句同樣,不利於系統維護。解決辦法就是將SQL語句和參數提取出來,寫在同一個xml文件中,若是須要修改,只須要修改配合文件便可,不須要修改Java源碼。
  • 從ResultSet結果集中獲取數據時,存在硬編碼,由於指定死了要獲取的字段數據。解決辦法就是將查詢到的結果集中的字段自動映射爲Java對象
  • 代碼重複問題,在上面的程序代碼中,獲取數據庫鏈接對象以及關閉釋放資源的代碼塊是不變的,惟一有變化的就在於執行SQL語句的代碼塊,若是開發多個SQL語句的程序,獲取數據庫鏈接對象以及關閉釋放資源的代碼塊將會高度重複,因此咱們能夠將這兩部分代碼塊提取出來,而將SQL語句和參數做爲一個參數進行傳入,而後執行

2. Mybatis框架介紹

    1. mybatis是一個持久層的框架,是apache下的頂級項目。mybatis讓程序將主要精力放在sql上,經過mybatis提供的映射方式,自由靈活生成(半自動化,須要程序員編寫sql語句的主體,而sql語句中的字段等參數由mybatis添加)知足須要sql語句。mybatis能夠將向 preparedStatement中的輸入參數自動進行輸入映射,將查詢結果集靈活映射成java對象(輸出映射)。程序員

    2. 框架執行流程:sql

    3. 詳細介紹及部分原理能夠參考:MyBatis的架構設計以及實例分析數據庫

3. 框架使用的簡單實例

    1. 編寫Mybatis的配置文件:apache

<?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>
	<settings>
		<setting name="lazyLoadingEnabled" value="true"/>
		<setting name="aggressiveLazyLoading" value="false"/>
	</settings>
	<!-- 與Spring整合後environments標籤的全部內容都要刪除 -->
	<environments default="environment">
		<environment id="environment">
			<!-- 配置JDBC事務管理, mybatis進行事物控制mybatis-->
			<transactionManager type="JDBC" />
			<!-- 配置數據庫鏈接池,使用mybatis提供的-->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url"
					value="jdbc:mysql://localhost:3306/cloud_note?useUnicode=true&amp;characterEncoding=utf8" />
				<property name="username" value="root" />
				<property name="password" value="123456" />
			</dataSource>
		</environment>
	</environments>
	<!--指定映射文件位置,與Spring整合後,只須要保存該標籤便可-->
	<mappers>
		<mapper resource="mapper/Usermapper.xml"/>
	</mappers>
</configuration>

    2. 編寫User的pojo類,用做輸入或輸出映射,注意,該pojo類必須符合JavaBean規範,並且類中的屬性名要與數據庫表中的字段名相同session

import java.io.Serializable;
public class User implements Serializable{
	private String cn_user_id;
	private String cn_user_name;
	private String cn_user_password;
	private String cn_user_token;
	private String cn_user_nick;
	public String getCn_user_id() {
		return cn_user_id;
	}
	public void setCn_user_id(String cn_user_id) {
		this.cn_user_id = cn_user_id;
	}
	public String getCn_user_name() {
		return cn_user_name;
	}
	public void setCn_user_name(String cn_user_name) {
		this.cn_user_name = cn_user_name;
	}
	public String getCn_user_password() {
		return cn_user_password;
	}
	public void setCn_user_password(String cn_user_password) {
		this.cn_user_password = cn_user_password;
	}
	public String getCn_user_token() {
		return cn_user_token;
	}
	public void setCn_user_token(String cn_user_token) {
		this.cn_user_token = cn_user_token;
	}
	public String getCn_user_nick() {
		return cn_user_nick;
	}
	public void setCn_user_nick(String cn_user_nick) {
		this.cn_user_nick = cn_user_nick;
	}
	@Override
	public String toString() {
		return "User [cn_user_id=" + cn_user_id + ", cn_user_name=" + cn_user_name + "]";
	}
	
}

    3. 編寫映射文件Mapper.xml:映射文件中包含了Java對象和數據庫表之間的映射關係,SQL語句所須要的輸入參數映射和輸出參數映射都在Mapper.xml中定義,該文件命名必須爲*Mapper.xml,好比UserMapper.xmlmybatis

<?xml version="1.0" encoding="UTF-8" ?>  
<!-- 首先要引入mybatis的標籤約束 -->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace屬性值表示命名空間,用於SQL的隔離-->
<mapper namespace="dao.UserDao">

	<!--select標籤就表示定義select查詢SQL語句,id就表示該語句的標示,當引用該語句時,就經過namespace + .id來引用該SQL語句  -->
	<select id="findUserById" parameterType="java.lang.String" resultType="entity.User">
		select * from cn_user where cn_user_id=#{id}
	</select>

	<!--insert標籤就表示定義insert插入數據SQL語句  -->
	<insert id="insertUser" parameterType="entity.User">
		<!-- selectKey標籤爲insert標籤特有的子標籤,用來將生成的主鍵回寫到傳入的pojo對象中
		 1. keyProperty指定回寫的pojo對象中的屬性名
		 2. order指定該回寫操做是在SQL語句執行前仍是執行後
		 3. resultType指定回寫的數據映射的Java類型
		 -->
		<selectKey keyProperty="cn_user_id" order="BEFORE" resultType="string">
		<!-- 經過mysql內置的UUID()函數來生成主鍵並注入至傳入User類型對象的cn_user_id屬性中
		而後再將此對象做爲參數傳入sql語句執行 -->
			select uuid()
		</selectKey>
		<!-- 在自增id的數據庫中可以使用last_insert_id()函數來獲取最後一次執行插入數據的主鍵id
		在執行完SQL語句後,將新插入數據的id返回給對象而且設置爲cn_user_id
		注意:自增主鍵的數據庫的主鍵id其類型必須爲int型
		<selectKey keyProperty="cn_user_id" order="AFTER" resultType="int">
			select last_insert_id()
		</selectKey>
		 -->
		insert into cn_user(cn_user_id,cn_user_name,cn_user_password,cn_user_token,cn_user_nick) 
		values(#{cn_user_id},#{cn_user_name},#{cn_user_password},#{cn_user_token},#{cn_user_nick})
		<!--#{}至關於JDBC代碼中的SQL預編譯語句中的 ? ,表示佔位符,若是接受的參數類型數parameterType
		爲Java中string、int、long等,則#{}中的字符串能夠隨便寫,但通常建議寫爲該參數對應的數據庫表的字段名;
		但若是該參數爲自定義的pojo類,如User類,則#{}中的字符串
		必須爲對應的pojo對象中的屬性名,Mybatis會經過OGNL獲取pojo對象中對應屬性的值-->
	</insert>

	<!-- 經過cn_user_name字段進行模糊查詢,可能會返回多條數據,可是每條數據所映射的對象仍然是entity.User類,resultType指的是每一條數據所映射的類型 -->
	<select id="selectUserLike" parameterType="string" resultType="entity.User">
		<!-- 該方式是將%寫在SQL語句中,那麼代碼中就不用在傳入的字符串兩端添加% -->
		select * from cn_user where cn_user_name like '%#{name}%'
		<!-- 該方式是在代碼對傳入的參數字符串兩端添加% -->
		<!-- select * from cn_user where cn_user_name like '#{name}' -->
	</select>
</mapper>

    4. 編寫測試程序:架構

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
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.Before;
import org.junit.Test;

import entity.User;
//Mybatis框架底層實現數據庫交互操做過程
public class TestCase1 {
	private SqlSessionFactory ssf;
	@Before
	public void init() throws IOException{
		//1.實例SqlSessionFactoryBuilder
		String resource="conf/SqlMapConfig.xml";
		InputStream s=Resources.getResourceAsStream(resource);
		SqlSessionFactoryBuilder ssfb=new SqlSessionFactoryBuilder();
		//2.經過SqlSessionFactoryBuilder對象加載mybatis環境配置文件,獲取SqlSessionFactory對象
		ssf=ssfb.build(s);
	}
	@Test
	public void testFindUserbyId(){
		//3.經過SqlSessionFactory對象來獲取SqlSession對象,與數據庫進行交互
		SqlSession session=ssf.openSession();
		User user=session.selectOne("dao.UserDao.findUserById", "03590914-a934-4da9-ba4d-b41799f917d1");
		System.out.println(user.toString());
		session.close();
	}
	@Test
	public void testInsertUser(){
		User user=new User();
		user.setCn_user_name("hop");
		user.setCn_user_password("123546");
		SqlSession session=ssf.openSession();
		session.insert("dao.UserDao.insertUser", user);
		System.out.println(user.toString());//查看回寫的用戶id
		session.close();
	}
	@Test
	public void testSelectUserLike(){
		SqlSession session=ssf.openSession();
		List<User> list=session.selectList("dao.UserDao.selectUserLike", "測試");
		//List<User> list=session.selectList("dao.UserDao.selectUserLike", "%測試%");
		session.close();
	}
}

    5. 關於Mapper映射文件中的部分重點講解:app

  • #{ }:表示一個佔位符號,#{}接收輸入參數,類型能夠是簡單類型,如integer、long等,若是接收簡單類型,#{}中能夠寫成value或其它名稱;

    若是輸入參數類型爲pojo類,並且該類中還包括一個pojo類型的屬性,#{}接收pojo對象值,經過OGNL讀取對象中的屬性值,經過屬性.屬性.屬性...的方式獲取對象屬性值,好比輸入參數類型爲User類,User類內還有一個book屬性,該屬性爲Book類,是一個pojo類,Book類中包括String類型的name屬性,那麼若是要取name屬性值,就能夠寫爲#{book.name}。

4. 經過Mybatis開發Dao

4.1 經過原始dao開發

    1. 定義Dao接口:

import entity.User;
public interface UserDao {
	public User findUserById(String id);
	public void insertUser(User user);
}

    2. 經過Mybatis開發Dao接口的實現類:

import java.io.IOException;
import java.io.InputStream;
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.Before;
import entity.User;
public class UserDaoImp implements UserDao {
	private static SqlSessionFactory ssf;
	static{
		String resource="conf/SqlMapConfig.xml";
		InputStream s;
		try {
			s = Resources.getResourceAsStream(resource);
			ssf=new SqlSessionFactoryBuilder().build(s);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	@Override
	public User findUserById(String id) {
		SqlSession session=ssf.openSession();
		User user=session.selectOne("findUserById",id);
		session.close();
		return user;
	}
	@Override
	public void insertUser(User user) {
		SqlSession session=ssf.openSession();
		session.insert("insertUser", user);
		session.close();
	}
}

    3. 原始dao開發存在的問題:

  • dao接口實現類方法中存在大量模板方法,設想可否將這些代碼提取出來,大大減輕程序員的工做量。
  • 調用sqlsession方法時將statement的id硬編碼了
  • 調用sqlsession方法時傳入的變量,因爲sqlsession方法使用泛型,即便變量類型傳入錯誤,在編譯階段也不報錯,不利於程序員開發。

4.2 Mapper代理開發

    1. 該只須要編寫好Mapper接口(Mapper接口也就是Dao接口),定義數據操做方法便可,編寫mapper接口須要遵循一些開發規範,mybatis能夠自動生成mapper接口實現類代理對象。

    2. 開發規範有:

  • 在mapper.xml中,namespace屬性值應爲mapper接口的全限定名
    package dao;
    import entity.User;
    public interface UserDao {
    	public User findUserById(String id);
    	public void insertUser(User user);
    }
    <?xml version="1.0" encoding="UTF-8" ?>  
    <!-- 首先要引入mybatis的標籤約束 -->
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="dao.UserDao">
    
    </mapper>

     

  • mapper.java接口中的方法名和mapper.xml中statement的id一致
    <?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="dao.UserDao">
    	
    	<select id="findUserById" parameterType="java.lang.String" resultType="entity.User">
    		select * from cn_user where cn_user_id=#{id}
    	</select>
    	
    	<insert id="insertUser" parameterType="entity.User">
    		<selectKey keyProperty="cn_user_id" order="BEFORE" resultType="string">
    			select uuid()
    		</selectKey>
    		insert into cn_user(cn_user_id,cn_user_name,cn_user_password,cn_user_token,cn_user_nick) 
    		values(#{cn_user_id},#{cn_user_name},#{cn_user_password},#{cn_user_token},#{cn_user_nick})
    	</insert>
    </mapper>

     

  • mapper.java接口中的方法輸入參數類型和mapper.xml中statement的parameterType指定的類型一致。
  • mapper.java接口中的方法返回值類型和mapper.xml中statement的resultType指定的類型一致。

    3. 知足以上開發規範後,可經過測試代碼驗證:

/**
 * 測試Mybatis框架經過Mapper代理方法實現Dao接口
 */
public class TestCase3 {
	private SqlSessionFactory ssf;
	@Before
	public void init() throws IOException{
		String resource="conf/SqlMapConfig.xml";
		InputStream s=Resources.getResourceAsStream(resource);
		SqlSessionFactoryBuilder ssfb=new SqlSessionFactoryBuilder();
		ssf=ssfb.build(s);
	}
	@Test
	public void testFindUserbyId(){
		SqlSession session=ssf.openSession();
		UserDao dao=session.getMapper(UserDao.class);
		User user=dao.findUserById("03590914-a934-4da9-ba4d-b41799f917d1");
		System.out.println(user.toString());
	}
}

    4. Mapper接口方法的參數只能有一個:這個問題其實很簡單,就是經過包裝類將所須要的多個參數包裝成一個類

相關文章
相關標籤/搜索