1. 程式人生 > >MybatisDay01(基礎配置 自定義sql片段、resultMap使用、動態sql)

MybatisDay01(基礎配置 自定義sql片段、resultMap使用、動態sql)

mybatis
基礎知識
對原生態jdbc程式(單獨使用jdbc開發)問題總結
mybatis框架原理

mybatis入門程式
	使用者的增、刪、改、查
mybatis開發dao兩種方法
	原始dao開發方法(程式需要編寫dao介面和dao實現類)
	mybatis的mapper介面(相當於dao介面)代理開發方法

mybatis配置檔案SqlMapConfig.xml
mybatis核心
	mybatis輸入對映
	mybatis輸出對映

mybatis的動態sql

高階知識
	訂單商品資料模型分析
	高階結果集對映(一對一、一對多、多對多)
	mybatis延遲載入
	mybatis查詢快取(一級快取、二級快取)
	mybatis和spring進行整合
	mybatis逆向工程

1、對原生態jdbc程式中問題總結
1.1、環境
java環境:jdk1.8
eclipse:oxygen
mysql:5.5
1.2、建立mysql資料
匯入sql指令碼
sql_table.sql:記錄表結構
sql_data.sql:記錄測試資料,在實際企業開發中,最後提供一個初始化資料指令碼

user表

1.3、jdbc程式
使用jdbc查詢mysql資料庫中使用者表的記錄
在匯入相關類的時候使用java.sql開頭的介面 因為以後可能要用oracle的所以java.sql泛用性強
建立java工程,加入jar包

資料庫驅動包(mysql5.5)
mysql-connector-java-5.1.5.jar
ojdbc6.jar

上邊是mysql驅動
下邊是oracle的驅動

JdbcConnection

public class JdbcConnection {
	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?characterEncoding=utf-8","root","123");
			
			//定義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) {
					
					e.printStackTrace();
				} finally {
					resultSet = null;
				}
			}
			if(preparedStatement != null) {
				try {
					preparedStatement.close();
				} catch (SQLException e) {
					
					e.printStackTrace();
				}finally {
					preparedStatement = null;
				}
			}
			if (connection != null ) {
				try {
					connection.close();
				} catch (SQLException e) {
					
					e.printStackTrace();
				} finally {
					connection = null;
				}
			}
		}
	}
}
使用jdbc的原始方法(未經封裝)實現了查詢資料庫表記錄的操作

1.4、問題總結
硬編碼就是寫死了的語句,不利於系統維護

	1、資料庫連線,使用時就建立,不實用立即釋放,對資料庫進行頻繁的連線開啟和關閉,造成資料庫資源浪費,影響資料庫效能。
	
	設想:使用資料庫連線池管理資料庫連線。

	2、將sql語句硬編碼到java程式碼中,如果sql語句修改,需要重新編譯java程式碼,不利於系統維護。

	設想:將sql語句配置在xml配置檔案中,即是sql變化,不需要對java程式碼進行重新編譯

	3、向preparedStatement中設定引數,對佔位符位置和設定引數,硬編碼在java程式碼中,不利於系統維護。

	設想:將sql語句及佔位符和引數全部配置在xml中

	4、從resultSet中遍歷結果集資料時,存在硬編碼,將獲取表的欄位進行硬編碼,不利於系統維護。

	設想:將查詢的結果集,自動對映成java物件。

2、mybatis框架
2.1、mybatis是什麼?
	mybatis是一個持久層的框架,是apache下的頂級專案

	mybatis託管到googlecode下,再後來脫管到github下(https://github.com/mybatis/mybatis-3/releases)。

在這裡插入圖片描述

	mybatis-3.4.6.jar---mybatis的核心包
	lib---mybatis的依賴包
	mybatis-3.4.6.pdf---mybatis使用手冊
	
	mybatis讓程式將主要精力放在sql上,通過mybatis提供的對映方式,自由靈活生成(半自動化,大部分需要程式設計師編寫sql)滿足需要sql語句

	mybatis可以將向preparedStatement中的輸入引數自動進行輸入對映,將查詢結果集靈活對映成java物件(輸出對映)

2.2、mybatis框架

在這裡插入圖片描述

3、入門程式
3.1、需求
	根據使用者id(主鍵)查詢使用者資訊
	根據使用者名稱稱模糊查詢使用者資訊
	新增使用者
	刪除使用者
	更新使用者

3.2、環境
	java環境jdk1.8
	eclipse:oxygen
	mysql:5.5
	mybatis執行環境(jar包)
	從https://github.com/mybatis/mybatis-3/releases下載
	加入mysql驅動包
3.3、log4j.properties
	#Global logging configuration
	#在開發環境下日誌級別要設定成DEBUG 如果設定成info不會報出錯誤資訊而是一般要顯示的資訊比如登入登出
	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

log4j.properties詳解

3.4、工程結構

在這裡插入圖片描述

3.5、SqlMapConfig.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/mybatis?characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
SqlMapConfig.xml是mybatis核心配置檔案,上邊檔案的配置內容為資料來源、事務管理

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語句
			需求:通過id查詢使用者表的記錄
			通過select執行資料庫查詢
			id:標識對映檔案中的sql
			將sql語句封裝到mappedStatement物件中,所以將id稱為statement的id
		 	#{}表示一個佔位符號
		 	#{}:其中的id表示接入輸入的引數,引數名稱就是id,如果輸入引數是簡單型別,#{}中的引數名可以任意,可以value或者其他名稱
		 	resultType:指定sql輸出結果所對映的java物件型別,select指定resultType表示將單條記錄對映成的java物件
		 -->
		 <select id="findUserById" parameterType="int" resultType="my.mybatis.po.User">
		 	SELECT * FROM USER WHERE username=#{value}
		 </select>
	</mapper>

3.6.3在SqlMapConfig.xml中載入對映檔案
    <!-- 載入對映檔案 -->
    <mappers>
    	<mapper resource="sqlmap/User.xml"/>
    </mappers>

3.6.4、程式編寫
 	//根據id查詢使用者資訊,得到一條記錄結果
	@Test
	public void findUserByIdTest() throws IOException {
		//mybatis配置檔案
		String resource = "SqlMapConfig.xml";
		InputStream stream = Resources.getResourceAsStream(resource);
		//建立會話工廠,傳入mybatis的配置檔案資訊
		SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(stream);
		
		//通過工廠得到SqlSession
		SqlSession sqlSession = sessionFactory.openSession();
		//通過SqlSession操作資料庫
		//第一個引數:對映檔案中statement的id,等於=namespace+"."+statement的id
		//第二個引數:指定和對映檔案中鎖匹配的parameterType型別的引數
		//sqlSession.selectOne結果是與對映檔案中所匹配的resultType型別的物件
		User user   = sqlSession.selectOne("test.findUserById", 1);
		System.out.println(user);
		sqlSession.close();
	}

3.7、根據使用者名稱稱模糊查詢使用者
3.7.1、對映檔案
	 <!-- 根據使用者名稱稱模糊查詢使用者資訊,可能返回多條
	 resulType:指定就是單條記錄所對映的java物件型別
	 ${}:表示拼接sql串,將接受到引數的內容不加任何修飾拼接在sql中
	 使用${}拼接sql,引起sql注入  因為可以新增任意字元所以會有特殊的sql被插入進去
	 ${value}:接受輸入引數的內容,如果傳入型別是簡單型別,${}只能使用value
	 	
	  -->
	 <select id="findUserByName" parameterType="java.lang.String" resultType="my.mybatis.po.User">
	 	SELECT * FROM USER WHERE username like '%${value}%'
	 </select>

3.7.2、程式程式碼
	//查詢使用者名稱稱模糊查詢使用者資訊
	@Test
	public void findUserByUsername() throws IOException {
		//mybatis配置檔案
		String resource = "SqlMapConfig.xml";
		InputStream stream = Resources.getResourceAsStream(resource);
		//建立會話工廠,傳入mybatis的配置檔案資訊
		SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(stream);
		
		//通過工廠得到SqlSession
		SqlSession sqlSession = sessionFactory.openSession();
		List<User> selectList = sqlSession.selectList("test.findUserByName", "小");
		System.out.println(selectList.toString());
	}

3.8、新增使用者
3.8.1、對映檔案
	在User.xml
	 <!-- 新增使用者
	 	parameterType:指定輸入引數是pojo(包括使用者資訊)
	 	#{}中指定pojo的屬性名,接收到pojo物件的屬性值 ,mybatis通過OGNL獲取物件的屬性值
	  -->
	<insert id="insertUser" parameterType="my.mybatis.po.User">
		insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address});
	</insert>
3.8.2、程式程式碼
	//插入使用者
	@Test
	public void insertUser() throws IOException, ParseException {
		// mybatis配置檔案
		String resource = "SqlMapConfig.xml";
		InputStream stream = Resources.getResourceAsStream(resource);
		// 建立會話工廠,傳入mybatis的配置檔案資訊
		SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(stream);

		// 通過工廠得到SqlSession
		SqlSession sqlSession = sessionFactory.openSession();
		//插入使用者物件
		User aUser = new User();
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
		Date parse = simpleDateFormat.parse("1921-10-21");
		aUser.setSex("男");
		aUser.setAddress("嗖嗖嗖");
		aUser.setBirthday(parse);
		aUser.setUsername("小採");
		//list中的user和對映檔案中resultType所指定的型別一致
		
		sqlSession.insert("test.insertUser", aUser);
		//提交事務
		sqlSession.commit();
		
		//獲取使用者資訊主鍵
		System.out.println(aUser.getId());
		
		//關閉會話
		sqlSession.close();
	}
3.8.3、自增主鍵返回
	mysql自增主鍵,執行insert提交之前自動生成一個自增主鍵。
	通過mysql函式獲取到剛插入記錄的自增主鍵:
		LAST_INSERT_ID()

	是insert之後呼叫此函式

	修改insert
	 <!-- 新增使用者
	 	parameterType:指定輸入引數是pojo(包括使用者資訊)
	 	#{}中指定pojo的屬性名,接收到pojo物件的屬性值 ,mybatis通過OGNL獲取物件的屬性值
	  -->
	<insert id="insertUser" parameterType="my.mybatis.po.User">
		<!-- 
			將插入資料的主鍵返回,返回到user物件中
			
			SELECT LAST_INSERT_ID();得到剛insert進去記錄的主鍵值,只適用於自增主鍵
			
			keyProperty:將查詢到主鍵值設定到parameterType指定的物件的那個屬性
			order:SELECT LAST_INSERT_ID():執行順序,相對於insert語句的執行順序
			resultType:指定select LAST_INSERT_ID()的結果型別
		 -->
		 <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>

3.8.4、非自增主鍵返回(使用uuid())
	使用mysql的uuid()函式生成主鍵,需要修改表中id欄位型別為string,長度設定為36位
		<!--
		 	使用mysql的uuid()生成主鍵
		 	執行過程:
		 		首先通過uuid()得到主鍵,將主鍵設定到user物件的id屬性中
		 		其次在insert執行時,從user物件中取出id屬性值 
		  -->
		 <selectKey keyProperty="address" order="BEFORE" resultType="java.lang.String">
		 	select uuid()
		 </selectKey>
    
    通過oracle的序列生成主鍵
    	<selectKey keyProperty="address" order="BEFORE" resultType="java.lang.String">
		 	select 序列名.nextval()
		 </selectKey>

3.9、刪除使用者
3.9.1、對映檔案
	<!-- 刪除使用者 
		根據id刪除使用者,需輸入id值
	-->
	<delete id="deleteUser" parameterType="java.lang.Integer">
		delete from user where id = #{id}
	</delete>

3.9.2、程式碼
	// 刪除使用者
	@Test
	public void deleteUser() throws IOException, ParseException {
		// mybatis配置檔案
		String resource = "SqlMapConfig.xml";
		InputStream stream = Resources.getResourceAsStream(resource);
		// 建立會話工廠,傳入mybatis的配置檔案資訊
		SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(stream);

		// 通過工廠得到SqlSession
		SqlSession sqlSession = sessionFactory.openSession();
		
		//傳入id刪除使用者
		sqlSession.delete("test.deleteUser", 1);
		// 提交事務
		sqlSession.commit();

		// 關閉會話
		sqlSession.close();
	}	

3.9、刪除使用者
3.9.1、對映檔案
	<!-- 根據id更新使用者
		分析:
			需要傳入使用者的id
			需傳入使用者的更新資訊
			paremeterType指定user物件,包括id和更新資訊,注意:id必須存在
				#{id}:從輸入user物件中獲取id屬性值
	 -->
	 <update id="updateUser" parameterType="my.mybatis.po.User">
	 	update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
	 </update>

3.9.2、程式碼
	// 更新使用者
	@Test
	public void updateUser() throws IOException, ParseException {
		// mybatis配置檔案
		String resource = "SqlMapConfig.xml";
		InputStream stream = Resources.getResourceAsStream(resource);
		// 建立會話工廠,傳入mybatis的配置檔案資訊
		SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(stream);
		
		// 通過工廠得到SqlSession
		SqlSession sqlSession = sessionFactory.openSession();
		User aUser = new User();
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
		Date parse = simpleDateFormat.parse("1921-10-21");
		aUser.setId(2);
		aUser.setSex("男");
//		aUser.setAddress("嗖嗖嗖");
		aUser.setBirthday(parse);
		aUser.setUsername("小1小採");
		//傳入id刪除使用者
		sqlSession.delete("test.updateUser", aUser);
		// 提交事務
		sqlSession.commit();
		
		// 關閉會話
		sqlSession.close();
	}

3.11、總結
3.11.1、parameterType
	在對映檔案中通過parameterType指定輸入引數的型別

3.11.2、resultType
	在對映檔案中通過resultType指定輸出結果的型別

3.11.3、#{}和${}
	#{}表示一個佔位符號,#{}接受輸入引數,型別可以是簡單型別:pojo、hashmap
	
	如果接受簡單型別,#{}中可以寫成value或其他名稱

	#{}接受pojo物件值,通過OGNL讀取物件中的屬性值,通過屬性.屬性.屬性...的方式獲取物件屬性值

	${}表示一個拼接符號,會引用sql注入,所以不建議使用${}

	${}接受輸入引數,型別可以是簡單型別,pojo、hashmap

	如果接受簡單型別,${}中只能寫成value

	${}接受pojo物件值,通過OGNL讀取物件中的屬性值,通過性.屬性.屬性...的方式獲取物件屬性值

3.11.4、selectOne和selectList
	selectOne表示查詢出一條記錄進行對映,如果使用selectOne可以實現使用selectList也可以實現(list中只有一個物件)

	selectList表示查詢出一個列表(多條記錄)進行對映,如果使用selectList查詢多條記錄,不能使用selectOne 
		報TooManyResultsException:期望1個但是有多個出現

3.12、mybatis和hibernate本質區別和應用場景
	hibernate:是一個標準ORM框架(物件關係框架)入門門檻較高,不需要程式寫sql,sql語句自動生成了。
	對sql語句進行優化、修改比較困難。

	應用場景:
		適用於需求變化不多的中小型專案,比如:後臺管理系統,erp、orm、oa。。

	mybatis:專注sql本身,需要程式設計師自己編寫sql語句,sql修改、優化比較方便。mybatis是一個不完全的ORM框架,
	雖然程式設計師自己寫sql,mybatis也可以實現對映(輸入對映、輸出對映)。

	應用場景:
		適用於需求變化較多的專案,比如:網際網路專案

	企業進行技術選型,以低成本高回報作為技術選型的原則,根據專案組的技術力量進行選擇。

4、mybatis開發dao的方法
4.1、SqlSession使用範圍
4.1.1、SqlSessionFactoryBuilder
	通過SqlSessionFactoryBuilder建立會話工廠SqlSessionFactory

	將SqlSesionFactoryBuilder:當成一個工具類使用即可,不需要使用單例管理SqlSessionFactoryBuilder

	在需要建立SqlSessionFactory時候,只需要new一次SqlSessionFactoryBuilder即可。


4.1.2、SqlSessionFactory
	通過SqlSessionFactory建立SqlSession,使用單例模式管理SqlSessionFactory(工廠一旦建立,使用一個例項)

	將來mybatis和spring整合後,使用單例模式管理SqlSessionFactory

4.1.3、SqlSession
	SqlSession是一個面向使用者(程式設計師)的介面

	SqlSession中提供了很多操作資料庫的方法:比如:selectOne(返回單個物件)、selectList(返回單個或多個物件)

	SqlSession是執行緒不安全的,在SqlSession實現類中除了有介面中的方法(操作資料庫的方法)還有資料域屬性
	(struts2多例管理 資料域屬性就是客戶傳入物件的username屬性之類)

	SqlSession最佳應用場合在方法體內(執行緒不安全建議放這),定義成區域性變數使用

4.2、原始dao開發方法(程式設計師需要寫dao介面和dao實現類)
4.2.1、思路
	程式設計師需要寫dao介面和dao實現類
	需要向dao實現類中注入SqlSessionFactory,在方法體內通過SqlSessionFactory建立SqlSession

4.2.2、dao介面
	public interface UserDao {
	//根據id查詢使用者資訊
	public User findUserById(int id) throws Exception;
	
	//新增使用者資訊
	public void insertUser(User user) throws Exception;
	
	//根據使用者名稱模糊查詢使用者列表
	public List<User> findUserByName(String name) throws Exception;
	
	//刪除使用者資訊
	public void deleteUSer(int id) throws Exception;
	}

4.2.3、dao介面實現類
	public class UserDaoImpl implements UserDao {
		
		//需要向dao實現類中注入SqlSessionFactory
		//通過構造方法注入
		private SqlSessionFactory sqlSessionFactory;
		public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
			this.sqlSessionFactory = sqlSessionFactory;
		}
		@Override
		public User findUserById(int id) throws Exception {
			SqlSession sqlSession = sqlSessionFactory.openSession();
			
			User user = sqlSession.selectOne("test.findUserById", id);
			//釋放資源
			sqlSession.close();
			
			return user;
		}
		
		@Override
		public List<User> findUserByName(String name) throws Exception {
			SqlSession sqlSession = sqlSessionFactory.openSession();
			
			List<User> list = sqlSession.selectList("test.findUserByName", name);
			
			//釋放資源
			sqlSession.close();
			
			return list;
		}
		
		@Override
		public void insertUser(User user) throws Exception {
			SqlSession sqlSession = sqlSessionFactory.openSession();
			
			sqlSession.insert("test.insertUser",user);
			
			//提交事務
			sqlSession.commit();
			
			//釋放資源
			sqlSession.close();
		}

		@Override
		public void deleteUSer(int id) throws Exception {
			SqlSession sqlSession = sqlSessionFactory.openSession();
			
			sqlSession.delete("test.deleteUSer",id);
			
			//提交事務
			sqlSession.commit();
			
			//釋放資源
			sqlSession.close();
		
		}

	}

4.2.4、測試程式碼
	public class UserDaoImplTest {
		//此方法執行在testFindUserById之前
		//單元測試
		private SqlSessionFactory sqlSessionFactory;
		@Before
		public void setUp() throws Exception {
			// 建立SqlSessionFactory
			// mybatis配置檔案
			String resource = "SqlMapConfig.xml";
			InputStream stream = Resources.getResourceAsStream(resource);
			// 建立會話工廠,傳入mybatis的配置檔案資訊
			//在例項前再加SqlSessionFactory會成為這個方法中的區域性變數不能被其他方法引用也就是上面定義的private會為空
			sqlSessionFactory = new SqlSessionFactoryBuilder()
					.build(stream);
		}

		@Test
		public void testFindUserById() throws Exception {
			//建立UserDao的物件
			UserDao userDao = new UserDaoImpl(sqlSessionFactory);
			
			//呼叫UserDao的方法
			List<User> user = userDao.findUserByName("小");
			
			System.out.println(user);
		}

	}

4.2.5、總結原始dao開發問題
1、dao介面實現類方法中存在大量模版方法,設想能否將這些程式碼提取出來,大大減輕程式設計師的工作量

2、呼叫sqlSession方法時將statement的id硬編碼了

3、呼叫sqlSession方法時傳入的變數,由於sqlSession方法使用泛型,即使變數型別傳入錯誤,在編譯階段也不報錯,
不利於程式設計師開發


4.3、mapper代理方法(程式設計師只需要mapper介面(相當於dao介面))
4.3.1、思路
	程式設計師只需要mapper介面(相當於dao介面)

	程式設計師還需要編寫mapper.xml對映檔案

	程式設計師編寫mapper介面需要遵循一些開發規範,mybatis可以自動生成mapper介面實現類代理物件

	開發規範:
		1、在mapper.xml中namespace等於mapper介面地址
			<!-- namespace名稱空間,作用就是對sql進行分類化管理,理解為sql隔離 
				使用mapper代理方法開發,namespace有特殊重要的作用,namespace等於mapper介面地址
			-->

			<mapper namespace="my.mybatis.mapper.UserMapper">

		2、mapper.java介面中的方法和mapper.xml中statement的id一致

		3、mapper.java介面中的方法輸入引數型別和mapper.xml中statement的parameterType指定的型別一致

		4、mapper.java介面中的方法返回值型別和mapper.xml中statement的resultType指定的型別一致
			<select id="findUserById" parameterType="int" resultType="my.mybatis.po.User">
			 	SELECT  * FROM USER WHERE id=#{value}
			 </select>

			//根據id查詢使用者資訊
			public User findUserById(int id) throws Exception;

總結:
	以上開發規範主要是對下邊程式碼進行統一生成
		User user = sqlSession.selectOne("test.findUserById", id);
		sqlSession.insert("test.insertUser",user);
		crud方法

4.3.2、mapper.java
	public interface UserMapper {
	
		//根據id查詢使用者資訊
		public User findUserById(int id) throws Exception;
		
	//	//新增使用者資訊
	//	public void insertUser(User user) throws Exception;
	//	
	//	//根據使用者名稱模糊查詢使用者列表
	//	public List<User> findUserByName(String name) throws Exception;
	//	
	//	//刪除使用者資訊
	//	public void deleteUSer(int id) throws Exception;
	}

4.3.3、mapper.xml
	 <select id="findUserById" parameterType="int" resultType="my.mybatis.po.User">
	 	SELECT  * FROM USER WHERE id=#{value}
	 </select>

4.3.4、在SqlMapConfig.xml中載入mapper.xml
	<mapper resource="mapper/UserMapper.xml"/>

4.3.5、測試
	public class UserMapperTest {
		// 此方法執行在testFindUserById之前
		// 單元測試
		private SqlSessionFactory sqlSessionFactory;

		@Before
		public void setUp() throws Exception {
			// 建立SqlSessionFactory
			// mybatis配置檔案
			String resource = "SqlMapConfig.xml";
			InputStream stream = Resources.getResourceAsStream(resource);
			// 建立會話工廠,傳入mybatis的配置檔案資訊
			// 在例項前再加SqlSessionFactory會成為這個方法中的區域性變數不能被其他方法引用也就是上面定義的private會為空
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
		}

		@Test
		public void testFindUserById() throws Exception {
			SqlSession sqlSession = sqlSessionFactory.openSession();
			
			//建立UserMapper物件,mybatis自動生成mapper代理物件
			UserMapper mapper = sqlSession.getMapper(UserMapper.class);
			
			//呼叫userMapper的方法
			User user = mapper.findUserById(2);
			System.out.println(user);
		}

	}

4.3.6、一些問題總結
4.3.6.1、代理物件內部呼叫selectOne或selectList
	如果mapper方法返回單個pojo物件(非集合物件),代理物件內部通過selectOne查詢資料庫

	如果mapper方法返回集合物件,代理物件內部通過selectList查詢資料庫

4.3.6.2、mapper介面方法引數只有一個是否影響系統開發
	mapper介面方法引數只能有一個,系統是否不利於擴充套件維護

	系統框架中,dao層的程式碼是被業務層公用的

	即使mapper介面只有一個引數,可以使用包裝型別的pojo滿足不同的業務方法的需求。

	注意:持久層方法的引數可以包裝型別、map。。。,service方法中建議不要使用包裝型別(不利於業務層的可擴充套件)

5、SqlMapConfig.xml
	mybatis的全域性配置檔案SqlMapConfig.xml,配置內容和順序如下:
		properties(屬性)
		settings(全域性配置引數)
		typeAliases(類型別名)
		typeHandlers(型別處理器)
		objectFactory(物件工廠)
		plugins(外掛)
		environments(環境集合屬性物件)
			environment(環境子屬性物件)
				transactionManager(事務管理)
				dataSource(資料來源)
		mappers(對映器)
    
	mybatis將按照下面的順序來載入屬性:
		在properties元素體內定義的屬性首先被讀取

		然後會讀取properties元素中resource或url載入的屬性,它會覆蓋已讀取的同名屬性

		最後讀取parameterType傳遞的屬性,它會覆蓋已讀取的同名屬性

	因此,通過parameterType傳遞的屬性具有最高優先順序,resource或url載入的屬性次之,最低優先順序的是properties元素體內定義的屬性。

	建議:
		不要在properties元素體內新增任何屬性值,只將屬性值定義在properties檔案中。

		在properties檔案中定義屬性名要有一定的特殊性,如xxxx.xxx.xxx

5.1、properties屬性
需求:
	將資料庫連線引數單獨配置在db.properties中,只需要在SqlMapConfig.xml中載入db.properties的屬性值,
	在SqlMapConfig.xml中就不需要對資料庫連線引數硬編碼

	將資料庫連線引數只配置在db.properties中,原因:方便對引數進行統一管理,其他xml可以引用該db.properties。
	配在properties有利於以後叢集的時候統一修改(利用指令碼等)

	jdbc.driver=com.mysql.jdbc.Driver
	jdbc.url=jdbc:mysql://localhost:3306/mybatis
	jdbc.username=root
	jdbc.password=123

	測試不僅要正確(預期)也要出現錯誤(故意修改)的現象

	在SqlMapConfig.xml載入屬性檔案
			<!-- 載入配置檔案 -->
			<properties resource="db.properties"></properties>
			<!-- 和spring整合後 environments將廢除 -->
			    <environments default="development">
			        <environment id="development">
			        <!-- 使用JDBC事務管理,事務控制由mybatis -->
			            <transactionManager type="JDBC"/>
			            <!-- 資料庫連線池,由mybatis管理 -->
			            <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>

5.2、settings全域性引數配置
	mybatis框架在執行時可以調整一些執行引數

	比如:開啟二級快取、開啟延遲載入

	比如:mybatis全域性配置引數,全域性引數將會影響mybatis的執行行為

settings

5.3、typeAliases(別名)
5.3.1、需求
	在mapper.xml中,定義很多的statement,statement需要parameterType指定輸入引數的型別,
	需要resultType指定輸出結果的對映型別

	如果在指定型別時輸入型別全路徑,不方便進行開發,可以針對parameterType或resultType指定的型別定義一些別名,
	在mapper.xml中通過別名定義,方便開發。

5.3.2、mybatis預設支援別名

別名Handlers


5.3.3、自定義別名
5.3.3.1、單個別名定義
	<!-- 別名定義 -->
	<typeAliases>
		<!-- 針對單個別名定義
		type:型別的路徑 
		alias:別名
		 -->
		<typeAlias type="my.mybatis.po.User" alias="user"/>
	</typeAliases>

	引用別名:
		<select id="findUserById" parameterType="int" resultType="user">
	 		SELECT  * FROM USER WHERE id=#{value}
	 	</select>

5.3.3.2、批量定義(常用)
	<!-- 批量別名定義 
	指定包名,mybatis自動掃描包中的po類,自動定義別名,別名就是類名(首字母大寫或小寫都可以)
	-->
	<package name="my.mybatis.po"/>

5.4、typeHandlers(型別處理器)
	mybatis通過typeHandlers完成jdbc型別和java型別的轉換

	通常情況下,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型別
	ClobReaderTypeHandler java.io.Reader
	ClobTypeHandler 	  java.lang.String 		CLOB,LONGVARCHAR
	NStringTypeHandler 	  java.lang.String NCLOB
	BlobInputStreamTypeHandler java.io.InputStream
	ByteArrayTypeHandler  byte[] 				資料庫相容的位元組流型別
	BlobTypeHandler 	  byte[] 				BLOB,LONGVARBINARY
	DateTypeHandler 	  java.util.Date  		TIMESTAMP
	DateOnlyTypeHandler   java.util.Date Date
	TimeOnlyTypeHandler   java.util.Date TIME
	SqlTimestampTypeHandler Timestamp(java.sql) TIMESTAMP型別
	SqlDateTypeHandler 	  Date(java.sql)		ATE型別
	SqlTimeTypeHandler 	  Time(java.sql) 		TIME型別
	ObjectTypeHandler 	  任意 					其他或未指定型別
	ENumTypeHandler 	  Enumeration型別 		VARCHAR任何相容的字串型別,作為程式碼儲存(而不是索引)

5.5、mappers(對映配置)
5.5.1、通過resource載入單個對映檔案
	<!-- 通過resource方法一次載入一個配置檔案 -->
	<mapper resource="mapper/UserMapper.xml"/>

5.5.2、通過mapper介面載入
	<!-- 通過mapper介面載入對映檔案 
	遵循一些規範:需要將mapper介面類名和mapper.xml對映檔名稱名保持一致,且在一個目錄中
	上邊規範的前提是:使用的是mapper代理方法
	-->
	<mapper class="my.mybatis.mapper.UserMapper" />

	按照上邊的規範,將mapper.java和mapper.xml放在一個目錄,且同名

在這裡插入圖片描述

5.5.3、批量載入mapper(推薦)
	<!-- 批量載入mapper
	指定mapper介面的包名,mybatis自動掃描包下變所有的mapper介面進行載入
	遵循一些規範:需要將mapper介面類名和mapper.xml對映檔名稱名保持一致,且在一個目錄中
	上邊規範的前提是:使用的是mapper代理方法
	 -->
	 <package name="my.mybatis.mapper"/>

Mapper.xml對映檔案
mapper.xml對映檔案中定義了操作資料庫的sql,每個sql是一個statement,對映檔案是mybatis的核心

6、輸入對映
	通過parameterType指定輸入引數的型別,型別可以是簡單型別、hashmap、pojo的包裝型別

6.1、傳遞pojo的包裝物件
6.1.1、需求
	完成使用者資訊的綜合查詢,需要傳入查詢條件(可能包括使用者資訊、其他資訊,比如商品、訂單的)

6.1.2、定義包裝型別pojo
	針對上邊需求,使用自定義的包裝型別的pojo

	在包裝型別的pojo中將複雜的查詢條件包裝進去
	public class UserQueryVo {
		//保證所需要的查詢條件
		
		//使用者查詢條件
		private UserCustom userCustom;

		public UserCustom getUserCustom() {
			return userCustom;
		}

		public void setUserCustom(UserCustom userCustom) {
			this.userCustom = userCustom;
		}
		
		
		//可以包裝其他的查詢條件,訂單、商品
	}

6.1.3、mapper.xml
	在UserMapper.xml中定義使用者資訊綜合查詢(查詢條件複雜,通過高階查詢進行復雜關聯查詢)
	<!--使用者資訊綜合查詢
	#{userCustom.sex}:取出pojo包裝物件中性別值 
	#{userCustom.username}:取出pojo包裝物件中使用者名稱稱
	 -->
	 <select id="findUserById" parameterType="my.mybatis.po.UserQueryVo" resultType="my.mybatis.po.UserCustom">
	 	SELECT  * FROM USER WHERE user.sex = #{userCustom.sex} and user.username Like '%${userCustom.username}%'
	 </select>

6.1.4、mapper.java
	public interface UserMapper {
	
		//使用者資訊綜合查詢
		public List<UserCustom> findUserList(UserQueryVo userQueryVo) throws Exception;

6.1.5、測試程式碼
	public void testFindUserById() throws Exception {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		
		//建立UserMapper物件,mybatis自動生成mapper代理物件
		UserMapper mapper = sqlSession.getMapper(UserMapper.class);
		
		//建立包裝物件,設定查詢條件
		UserQueryVo userQueryVo = new UserQueryVo();
		UserCustom userCustom = new UserCustom();
		userCustom.setSex("男");
		userCustom.setUsername("小");
		//需要將資訊注入到QueryVo中否則空指標
		userQueryVo.setUserCustom(userCustom);
		//呼叫userMapper的方法
		List<UserCustom> list = mapper.findUserList(userQueryVo);
		System.out.println(list);
	}


7、輸出對映
7.1、resultType
	使用resultType進行輸出對映,只有查詢出來的列名和pojo中的屬性名一致,該列才可以對映成功。

	如果查詢出來的列名和pojo中的屬性名完全不一致,沒有建立pojo物件。

	只要查詢出來的列名和pojo中的屬性有一個一致,就會建立pojo物件

7.1.1、輸出簡單型別
7.1.1.1、需求
	使用者資訊的綜合查詢列表總數,通過查詢總數和上邊使用者綜合查詢列表才可以實現分頁

7.1.2、mapper.xml
	<!-- 使用者資訊綜合查詢總數
	parameterType:指定輸入型別和findUserList一樣
	resultType:輸出結果型別 
	 -->
	<select id="findUserCount" parameterType="my.mybatis.po.UserQueryVo" resultType="int">
	 	SELECT count(*) FROM USER WHERE user.sex = #{userCustom.sex} and user.username Like '%${userCustom.username}%'
	 </select>

7.1.1.3、mapper.java
	//使用者資訊綜合查詢總數
	public int findUserCount(UserQueryVo userQueryVo) throws Exception;

7.1.1.4、測試程式碼
	@Test
	public void testFindUserCount() throws Exception {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		
		//建立UserMapper物件,mybatis自動生成mapper代理物件
		UserMapper mapper = sqlSession.getMapper(UserMapper.class);
		
		//建立包裝物件,設定查詢條件
		UserQueryVo userQueryVo = new UserQueryVo();
		UserCustom userCustom = new UserCustom();
		userCustom.setSex("男");
		userCustom.setUsername("小");
		//需要將資訊注入到QueryVo中否則空指標
		userQueryVo.setUserCustom(userCustom);
		//呼叫userMapper的方法
		int list = mapper.findUserCount(userQueryVo);
		System.out.println(list);
	}

7.1.1.5、小結
	查詢出來的結果集只有一行且一列,可以使用簡單型別進行輸出對映

7.1.2、輸出pojo物件和pojo列表
	不管是輸出的pojo單個物件還是一個列表(list中包括pojo),在mapper.xml中resultType指定的型別是一樣的。
	在mapper.java指定的方法返回值型別不一樣
		1、輸出單個pojo物件,方法返回值是單個物件型別。
			public User findUserById(int id) throws Exception;

		2、輸出pojo物件list,方法返回值是List<Pojo>
			public List<User> findUserByName(String name) throws Exception;

	生成的動態代理物件中是根據mapper方法的返回值型別確定是呼叫selectOne(返回單個物件呼叫)還是selectList(返回集合物件呼叫)

輸出hashmap
	輸出pojo物件可以改用hashmap輸出型別,將輸出的欄位名稱(id,username等)作為map的key,value作為欄位值

7.2、resultMap
	resultType可以指定pojo將查詢結果對映為pojo,但需要pojo的屬性名和sql查詢的列名一致方可對映成功。

	mybatis中使用resultMap完成高階輸出結果對映

7.2.1、resultMap使用方法
	如果sql查詢欄位名和pojo的屬性名不一致,可以通過定義一個resultMap將欄位名和屬性名作一個對應關係,
	resultMap實質上還需要將查詢結果對映到pojo物件中。

	1、定義resultMap

	2、使用resultMap作為statement的輸出對映型別

	resultMap可以實現將查詢結果對映為複雜型別的pojo,比如在查詢結果對映物件中包括pojo和list實現一對一查詢和一對多查詢。

7.2.2、將下邊的sql使用UserCustom完成對映
	SELECT id id_,username username_ FROM USER WHERE id=#{value}

	userCustom類中屬性名和上邊查詢列名不一致

7.2.2.1、定義resultMap
	<!-- 定義resultMap
	將SELECT id id_,username username_ FROM USER 和User類中的屬性作一個對映關係
	
	type:resultMap最終對映的java物件型別,可以使用別名
	id:對resultMap的唯一標識
	 -->
	<resultMap type="user" id="userResultMap">
		<!-- id表示查詢結果集中唯一標識
		cloumn:查詢出來的裡面
		property:type指定的pojo型別中的屬性名
		最終resultMap對column和property作一個對映關係(對應關係)
		 -->
		<id column="id_" property="id"/>
		<!-- 
		result:對普通名對映定義
		column:查詢出來的列名
		property:type指定的pojo型別中的屬性名
		最終resultMap對column和property作一個對映關係(對應關係)
		 -->
		 <result column="username_" property="username"/>
	</resultMap>

7.2.2.2、使用resultMap作為statement的輸出對映型別
	<!-- 使用resultMap進行輸出對映
	resultMap:指定定義的resultMap的id,如果這個resultMap在其他的mapper檔案,前邊需要加namespace
	因為使用resultMap輸出對映所以別忘了修改resultType為resultMap
	否則會提示Could not resolve type alias 'userResultMap'.  Cause: java.lang.ClassNotFoundException: Cannot find class: userResultMap
	 -->
	<select id="findUserByResultMap" parameterType="int" resultMap="userResultMap">
	 	SELECT id id_,username username_ FROM USER WHERE id=#{value}
	 </select>

7.2.2.3、mapper.java
	//根據id查詢使用者資訊,使用resultMap輸出
	public User findUserByResultMap(int id) throws Exception;

7.2.2.4、測試
	@Test
	public void testFindUserByResultMap() throws Exception {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		
		//建立UserMapper物件,mybatis自動生成mapper代理物件
		UserMapper mapper = sqlSession.getMapper(UserMapper.class);
		
		//呼叫userMapper的方法
		User list = mapper.findUserByResultMap(2);
		System.out.println(list);
	}

7.3、小結
	使用resultType進行輸出對映,只有查詢出來的列名和pojo中的屬性名一致,該列才可以對映成功。

	如果查詢出來的列名和pojo的屬性名不一致,通過定義一個resultMap對列名和屬性名之間作一個對映關係。

8、動態sql
8.1、什麼是動態sql
	mybatis核心對sql語句進行靈活操作,通過表示式進行判斷,對sql進行靈活拼接、組裝

8.2、需求
	使用者資訊綜合查詢列表和使用者資訊查詢列表總數這兩個statement的定義使用動態sql

	對查詢條件進行判斷,如果輸入引數不為空才進行查詢條件拼接。

8.3、mapper.xml
	<!--使用者資訊綜合查詢
	#{userCustom.sex}:取出pojo包裝物件中性別值 
	#{userCustom.username}:取出pojo包裝物件中使用者名稱稱
	 -->
	 <select id="findUserList" parameterType="my.mybatis.po.UserQueryVo" resultType="my.mybatis.po.UserCustom">
	 	SELECT  * FROM USER 
	 	<!-- 
	 		where可以自動去掉條件中的第一個and
	 	 -->
	 	<where>
	 		<if test="userCustom!=null">
			 	<if test="userCustom.sex != null and userCustom.sex!=''">
			 		and user.sex = #{userCustom.sex}
			 	</if>
			 	<if test="userCustom.username != null and userCustom.username!=''">
			 		and user.username Like '%${userCustom.username}%'
			 	</if>
		 	</if>
	 	</where>
	 </select>

8.4、測試程式碼
	@Test
	public void testFindUserList() throws Exception {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		
		//建立UserMapper物件,mybatis自動生成mapper代理物件
		UserMapper mapper = sqlSession.getMapper(UserMapper.class);
		
		//建立包裝物件,設定查詢條件
		UserQueryVo userQueryVo = new UserQueryVo();
		UserCustom userCustom = new UserCustom();
		//由於這裡使用動態sql,如果不設定某個值,條件不會拼接在sql中
//		userCustom.setSex("男");
		userCustom.setUsername("小");
		//需要將資訊注入到QueryVo中否則空指標
		userQueryVo.setUserCustom(userCustom);
		//呼叫userMapper的方法
		List<UserCustom> list = mapper.findUserList(userQueryVo);
		System.out.println(list);
	}

8.5、sql片段
8.5.1、需求
	將上邊實現的動態sql判斷程式碼塊抽取出來,組成一個sql片段。其他的statement中就可以引用sql片段

8.5.2、定義sql片段
	<!-- 定義sql片段
	id:sql片段的唯一標識
	
	經驗:是基於單表來定義sql片段,這樣的話這個sql片段可重用性才高
	在sql片段中不要包括where 因為可能有多個and,判斷語句較多
	 -->
	<sql id="query_user_where">
		<if test="userCustom!=null">
		 	<if test="userCustom.sex != null and userCustom.sex!=''">
		 		and user.sex = #{userCustom.sex}
		 	</if>
		 	<if test="userCustom.username != null and userCustom.username!=''">
		 		and user.username Like '%${userCustom.username}%'
		 	</if>
	 	</if>
	</sql>

8.5.3、引用sql片段
	在mapper.xml中定義的statement中引用sql判斷
	<!--使用者資訊綜合查詢
	#{userCustom.sex}:取出pojo包裝物件中性別值 
	#{userCustom.username}:取出pojo包裝物件中使用者名稱稱
	 -->
	 <select id="findUserList" parameterType="my.mybatis.po.UserQueryVo" resultType="my.mybatis.po.UserCustom">
	 	SELECT  * FROM USER 
	 	<!-- 
	 		where可以自動去掉條件中的第一個and
	 	 -->
	 	<where>
	 		<!-- 引用sql片段的id,如果refid指定的id不再本mapper檔案中,需要前邊加namespace -->
			<include refid="query_user_where"></include>
			<!-- 在這裡還要引用其他的sql片段 -->
	 	</where>
	 </select>

8.6、foreach
	向sql傳遞陣列或list,mybatis使用foreach解析

8.6.1、需求
	在使用者查詢列表和查詢總數的statement中增加多個id輸入查詢

	sql語句如下:

	兩種方法:
		SELECT * FROM USER WHERE id = 1 OR id =10 OR id = 16

		SELECT * FROM USER WHERE id IN[1,10,16]

8.6.2、在輸入引數型別中新增List<Integer> ids 傳入多個id
	public class UserQueryVo {
	
	//傳入多個id
	private List<Integer> ids;

	public List<Integer> getIds() {
		return ids;
	}

	public void setIds(List<Integer> ids) {
		this.ids = ids;
	}

8.6.3、修改mapper.xml
	WHERE id = 1 OR id =10 OR id = 16

	在查詢條件中,查詢條件定義成一個sql片段,需要修改sql片段。
	 	<if test="ids != null ">
 		<!-- 
 			使用foreach遍歷傳入ids
 			collection:指定輸入物件中集合屬性
 			item:每個遍歷生成物件中
 			open:開始遍歷時拼接串
 		 -->
 		 <!-- 實現下邊的sql拼接
 		 AND (id = 1 OR id =10 OR id = 16)
 		  -->
 		<foreach collection="ids" item="user_id" open="AND (" close=")" separator="OR">
 			<!-- 每個遍歷需要拼接的串 -->
 			id=#{user_id}
 		</foreach>

 8.6.4、測試程式碼
	@Test
	public void testFindUserList() throws Exception {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		
		//建立UserMapper物件,mybatis自動生成mapper代理物件
		UserMapper mapper = sqlSession.getMapper(UserMapper.class);
		
		//建立包裝物件,設定查詢條件
		UserQueryVo userQueryVo = new UserQueryVo();
		UserCustom userCustom = new UserCustom();
		//由於這裡使用動態sql,如果不設定某個值,條件不會拼接在sql中
//		userCustom.setSex("男");
		userCustom.setUsername("小");
		ArrayList<Integer> ids = new ArrayList<Integer>();
		ids.add(1);
		ids.add(2);
		//需要將資訊注入到QueryVo中否則空指標
		userQueryVo.setIds(ids);
		userQueryVo.setUserCustom(userCustom);
		//呼叫userMapper的方法
		List<UserCustom> list = mapper.findUserList(userQueryVo);
		System.out.println(list);
	}

另一個sql的實現
	 <!-- 實現下邊的sql拼接
	 AND id in(1,10,16)
	  -->
	<foreach collection="ids" item="user_id" open=" AND id in (" close=")" separator=",">
		<!-- 每個遍歷需要拼接的串 -->
		#{user_id}
	</foreach>