Every MyBatis application centers around aninstance of SqlSessionFactory. A SqlSessionFactoryinstance can be acquired by using
the SqlSessionFactoryBuilder.
SqlSessionFactoryBuildercan builda SqlSessionFactoryinstance from an XML configuration file, of from a custom prepared instance ofthe
Configuration class.
這是MyBatis使用文檔2.1中的內容。他講述了SqlSessionFactory的至關重要的地位,就如同Hibernate的SessionFactory。它存在一個工廠類:SqlSessionFactoryBuilder。SqlSessionFactory能夠通過工廠構造類執行個體化。如同Hibernate的SessionFactory,它的建立可以通過傳統的ConfigurationClass類也可以通過XML設定檔進行。
通過XML建立SqlSessionFactory
為了更好的具體瞭解,我們先建立一些列的檔案結構,如所示:
這些檔案結構是根據MVC的設計模式來進行的,dao層是對資料庫的訪問層,service是商務邏輯層,而proxy是對service的代理,其中最重要的是SessionManager,而proxy則是用這個SessionManger類建立產生的SqlSessionFactory開啟SqlSession,將SqlSession傳遞給Service,接著Service傳遞給Dao層的Impl實作類別來具體操作資料庫。之後在Proxy類中關閉資料流,即SqlSession。
將mybatis提供的xml複製到mybatis-config.xml設定檔中:
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><environments default="development"><environment id="development"><transactionManager type="JDBC" /><dataSource type="POOLED"><property name="driver" value="${driver}" /><property name="url" value="${url}" /><property name="username" value="${username}" /><property name="password" value="${password}" /></dataSource></environment></environments><mappers><mapper resource="org/mybatis/example/BlogMapper.xml" /></mappers></configuration>
可以看出,上面的設定檔並不完善,不過mybatis未做解釋,繼續向下。
MyBatis提供了XML方式和非XML方式建立SqlSessionFactory,而我們現在將的 是XML的方式,當然是通過這個方式了。
進行SessionManager的編寫:
package net.mybatis.manager;import java.io.IOException;import java.io.InputStream;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;public class SessionManager {//default xml pathprivate static String resource = "mybatis-config.xml";private static SqlSessionFactory sqlSessionFactory;public static SqlSessionFactory getFactory() throws IOException{InputStream inputStream = Resources.getResourceAsStream(resource);return (sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream));}public static String getResource() {return resource;}public static void setResource(String resource) {SessionManager.resource = resource;}public SqlSessionFactory getSqlSessionFactory() {return sqlSessionFactory;}}
我在這個SessionManager的類中提供了getSqlSessionFactory的方法,Proxy代理類可以通過這個方法通過openSession來擷取到SqlSession。
我採用的是自下向上的編寫方式。因此,接下來對User進行編碼(因為只是進行測試,所以User中只有一個name的屬性):
package net.mybatis.model;public class User {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}}
接下來,定義資料庫操作的介面IUserDao(這裡我定義了一個getUsers的方法):
package net.mybatis.dao;import java.util.List;import net.mybatis.model.User;public interface IUserDao {public List<User> getUsers();}
定義了介面的方法,那麼就要有個具體的實作類別:
package net.mybatis.dao.impl;import java.util.List;import org.apache.ibatis.session.SqlSession;import net.mybatis.dao.IUserDao;import net.mybatis.model.User;public class UserDaoImpl implements IUserDao{private SqlSession session;public UserDaoImpl(SqlSession sqlSession){this.session = sqlSession;}@Overridepublic List<User> getUsers() {return null;}}
因為我們不知道MyBatis如何通過session來具體操控資料庫,所以現在先放置在一邊。
另外,我們需要SqlSession來進行資料庫的操作,因此,要在IUserDao介面執行個體化的時候,傳進來SqlSession這個參數,以此操控資料庫。
接下來是商務邏輯層的介面定義:
package net.mybatis.service;import java.util.List;import net.mybatis.model.User;public interface IUserService {public List<User> getUsers();}
接著是商務邏輯層的具體實現:
package net.mybatis.service.impl;import java.util.List;import org.apache.ibatis.session.SqlSession;import net.mybatis.dao.IUserDao;import net.mybatis.dao.impl.UserDaoImpl;import net.mybatis.model.User;import net.mybatis.service.IUserService;public class UserServiceImpl implements IUserService{private IUserDao dao;public UserServiceImpl(SqlSession sqlSession){this.dao = new UserDaoImpl(sqlSession);}@Overridepublic List<User> getUsers() {return this.dao.getUsers();}}
Service層的實現是通過IUserDao層的執行個體化,傳入SqlSession,通過調用dao層的getUsers方法來擷取資料庫操控結果。
因為所有的Session是由Proxy進行代理的,所以,我們仍舊是通過建構函式傳入一個SqlSession,這樣也有利於減少耦合。
接下來最為重要,是Proxy的編寫,所有的業務處理都通過該類來調用實現:
package net.mybatis.proxy;import java.io.IOException;import java.util.ArrayList;import java.util.List;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import net.mybatis.manager.SessionManager;import net.mybatis.model.User;import net.mybatis.service.IUserService;import net.mybatis.service.impl.UserServiceImpl;public class UserProxy {private SqlSessionFactory sqlSessionFactory;private SqlSession sqlSession;private IUserService service;public UserProxy(){try {this.sqlSessionFactory = SessionManager.getFactory();} catch (IOException e) {e.printStackTrace();}this.sqlSession = this.sqlSessionFactory.openSession();this.service = new UserServiceImpl(this.sqlSession);}public List<User> getUsers() throws Exception{List<User> users = new ArrayList<User>();try{users = this.service.getUsers();}catch(Exception e){throw e;}finally{this.sqlSession.close();}return users;}}
接下來是MyBatis提供了兩種方式操控資料庫,第一種是session.selectOne這種直接通過SqlSession來操作的,另一種是通過一個介面檔案,該類檔案通常被以Mapper命名解為,比如UserMapper,這類方式,通過介面檔案中的介面方法來進行叫用作業。
MyBatis對這兩種方式做了一定的解釋。
這兩種方式,都可以通過XML或者Annotation的方式定義。
首先是XML的方式:
我們先前複製了簡單的mybatis.xml檔案,現在我們將其中的<mapper>標籤:
<mappers><mapper resource="org/mybatis/example/BlogMapper.xml" /></mappers>更改為:<mapper namespace="net.mybatis.mapper.UserMapper"><select id="selectUser" resultType="List">select * from User</select></mapper>
很明顯的可以看出這些標籤的意思:mapper中的namespace是用來標識Mapper這類的聯絡檔案的。而<select>標籤則是用來描述SQL語句,id為方法的方法名,resultType為方法的傳回型別,我們這裡是List<User>,自然的也有參數類型:parameterType。因為我們沒有參數,所以,這裡不需要填寫參數類型。
寫一個測試類別,net.mybatis.test.Test:
package net.mybatis.test;import net.mybatis.proxy.UserProxy;public class Test {public static void main(String []args) throws Exception{UserProxy proxy = new UserProxy();System.out.println(proxy.getUsers());}}
並且將UserDaoImpl中的getUsers的方法進行修改:
@Overridepublic List<User> getUsers() {List<User> users = this.session.selectList("net.mybatis.mapper.UserMapper.selectUser");return users;}
右擊運行,出現:
Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException: ### Error building SqlSession.### Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 17; columnNumber: 52; 必須為元素類型 "mapper" 聲明屬性 "namespace"。
的錯誤。
這是什麼原因?
仔細閱讀文檔,發現文檔一開始所描述的與之後的Configuration XML的章節中,配置的有出入,不知道是不是之前的方法過時的緣故還是什麼原因,暫時未找到原因,而根據後面文檔的配置,則是可以成功運行,那麼我們就以之後文檔的描述來進行設定。
之前的的問題找到原因了。是因為沒有將mapper另外獨立出來,MyBatis在config設定檔中不允許直接使用mapper中的select等操作,需要建立立一個xml檔案user.mapper.xml,該檔案格式為:
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="net.mybatis.mapper.UserMapper"><select id="selectUser" resultType="net.mybatis.model.User">select * from user</select></mapper>
可以看到,它的DOCTYPE為Mapper,而之前的config設定檔為Config,這個也就是為什麼會出現之前問題的原因。
但是因為MyBatis不能夠返回一個List,它只能返回基礎類型,List的返回是通過集合中的元素類型來決定傳回型別的。
將mybatis-cofing檔案中的<mappers>改為:
<mappers> <mapper resource="net/mybatis/mapper/user.mapper.xml"/></mappers>
將UserMapper更改為:
package net.mybatis.mapper;import java.util.List;import net.mybatis.model.User;public interface UserMapper {public List<User> selectUser();}
將UserDaoImpl更改為:
package net.mybatis.dao.impl;import java.util.List;import org.apache.ibatis.session.SqlSession;import net.mybatis.dao.IUserDao;import net.mybatis.model.User;public class UserDaoImpl implements IUserDao{private SqlSession session;public UserDaoImpl(SqlSession sqlSession){this.session = sqlSession;}@Overridepublic List<User> getUsers() {List<User> users = this.session.selectList("net.mybatis.mapper.UserMapper.selectUser");return users;}}
運行後的結果:
log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory).log4j:WARN Please initialize the log4j system properly.log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.[net.mybatis.model.User:[name:aa], net.mybatis.model.User:[name:dd]]
可以看到,資料庫的資料已經查了出來。