1. 程式人生 > >Mybatis動態資料來源實現

Mybatis動態資料來源實現

Mybatis資料庫檔案配置是在專案啟動時初始化資料工廠的,初始化過程僅為1次,當資料庫地址改變時需修改配置檔案重新啟動專案,無法動態載入資料來源。
Mybatis連線資料庫底層核心庫SqlSessionFactory,專案初始化也是生成該類,並快取,該需求需要通過程式設計根據不同資料來源動態生成SqlSessionFactory例項。
核心程式碼:

        String driver="oracle.jdbc.driver.OracleDriver",url="jdbc:oracle:thin:@127.0.0.1:1521:orcl";
        ///mysql
        //driver = "com.mysql.jdbc.Driver"
; //url = "jdbc:mysql://"+hotel.getIp()+":"+hotel.getPort()+"/"+hotel.getDatabase(); //初始化資料庫屬性 Properties properties = new Properties(); properties.setProperty("jdbc.driver",driver); properties.setProperty("jdbc.url", url); properties.setProperty("jdbc.username"
,hotel.getUsername()); properties.setProperty("jdbc.password",hotel.getPassword()); Reader reader = Resources.getResourceAsReader(HotelFactory.configuration); //建立資料工廠 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = builder.build
(reader, properties); SqlSession sqlSession = sqlSessionFactory.openSession(); HotelMapper hotelMapper = sqlSession.getMapper(HotelMapper.class); hotelMapper.getHotelById(1); //釋放會話 sqlSession.clearCache(); sqlSession.close();

configuration.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>
    <typeAliases>
        <!--給實體類起一個別名 user 
        <typeAlias type="entity.Hotel" alias="Hotel" />
        <typeAlias type="entity.Room" alias="Room" />
        -->
    </typeAliases>
    <!--資料來源配置  使用mysql資料庫 -->
    <environments default="HD">
        <environment id="HD">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <!-- 併發最大連線數 預設10-->
                <property name="poolMaximumActiveConnections" value="1000"/>
                <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>

<!--        <environment id="HO">  
            <transactionManager type="JDBC" />  
            <dataSource type="POOLED">  
                <property name="driver" value="oracle.jdbc.driver.OracleDriver" />  
                <property name="url" value="jdbc:oracle:thin:@192.168.1.17:1521:orcl" />  
                <property name="username" value="hotel" />  
                <property name="password" value="q123" />  
            </dataSource>  
        </environment>   -->
    </environments>
    <mappers>
        <!-- userMapper.xml裝載進來  同等於把“dao”的實現裝載進來 -->
        <mapper resource="mapper/HotelMapper.xml"/>
        <mapper resource="mapper/RestaurantMapper.xml"/>
        <mapper resource="mapper/RoomsMapper.xml"/>
        <mapper resource="mapper/MeetingsMapper.xml"/>
    </mappers>
</configuration> 

HotelMapper

<?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="mapper.HotelMapper">
    <select id="getHotelById" resultType="entity.Hotel">
        select * from HOTEL
        where ID = #{id,jdbcType=DECIMAL}
    </select>
</mapper>

    public interface HotelMapper {
        Hotel getHotelById(@Param("id") int id);
    }


SqlSessionFactory只是記載的資料庫的連線屬性,並未與資料庫連線,並不佔用資料庫及系統資源。

SQLSession便於資料庫建立連線了,每次讀取資料後若不釋放,資料庫的連線數不斷增加,當超過資料庫的最大連線數或者記憶體溢位均會導致程式崩潰。

每次連線都需要初始化,過於麻煩,可封裝套資料工廠,快取相關資訊。

設計思路1:快取SqlSessionFactory,通過java動態代理釋放資料庫資源即SqlSession。(適用多點,SqlSession數量不可控情況)
設計思路2:快取SqlSessionFactory及SqlSession,SqlSession會預設快取已呼叫的方法,通過sqlSession.clearCache()清空預設快取,保證讀取的是實時資料庫。(適用單點或少數站點機,SqlSession數量可控情況)

設計思路1核心程式碼:
資料工廠DataSourceFactory程式碼:

package factory;

import java.io.Reader;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

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 entity.Hotel;

@SuppressWarnings("finally")
public class DataSourceFactory {

    //酒店資料工作快取數量
    private static final int MaxDataSourceSize=10;
    //埠超時時間
    private static final int TimeOut = 2000;

    //酒店資料工廠快取
    private static Map<Integer,SqlSessionFactory> dataSoruce;

    static{
        dataSoruce = new HashMap<Integer, SqlSessionFactory>(MaxDataSourceSize);
    }

    //刪除第一個元素
    private static void removeFirstMap(){
        for (Integer key : dataSoruce.keySet()) {  
            dataSoruce.remove(key);
            break;
        }  
    }

    //刪除酒店快取
    public static void removeHotel(int hotelid){
        dataSoruce.remove(hotelid);
    }

    //檢測連線是否可用

    public static boolean isConnection(Hotel hotel){
        Boolean result = false;
        try{
            //檢查埠是否開放
            Socket client = new Socket();
            SocketAddress socketAddress = new InetSocketAddress(hotel.getIp(),Integer.parseInt(hotel.getPort()));
            client.connect(socketAddress,TimeOut);
            client.close();

            result = true;
        }
        catch(Exception e){
            e.printStackTrace();
            result = false;
        }
        finally{
            return result;
        }
    }

    public static boolean isConnection(Integer id){
        Boolean result = false;
        try{
            Hotel hotel = HotelFactory.getHotelById(id);
            if(hotel != null){
                //檢查埠是否開放
                Socket client = new Socket();
                SocketAddress socketAddress = new InetSocketAddress(hotel.getIp(),Integer.parseInt(hotel.getPort()));
                client.connect(socketAddress,TimeOut);
                client.close();
                result = true;
            }
        }
        catch(Exception e){
            e.printStackTrace();
            result = false;
        }
        finally{
            return result;
        }
    }

    private static SqlSessionFactory createSqlSessionFactory(Integer id){
        SqlSessionFactory _sqlSessionFactory = null;
        try{
            Hotel hotel = HotelFactory.getHotelById(id);
            if(hotel != null && isConnection(hotel)){
                //資料庫匹配           Oracle/Mysql
                String driver="oracle.jdbc.driver.OracleDriver",url="jdbc:oracle:thin:@"+hotel.getIp()+":"+hotel.getPort()+":"+hotel.getSid();
                ///mysql
                //driver = "com.mysql.jdbc.Driver";
                //url = "jdbc:mysql://"+hotel.getIp()+":"+hotel.getPort()+"/"+hotel.getDatabase();
                //初始化資料庫屬性
                Properties properties = new Properties();
                properties.setProperty("jdbc.driver",driver);  
                properties.setProperty("jdbc.url", url);  
                properties.setProperty("jdbc.username",hotel.getUsername());  
                properties.setProperty("jdbc.password",hotel.getPassword());
                Reader reader = Resources.getResourceAsReader(HotelFactory.configuration);
                //建立資料工廠
                SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
                _sqlSessionFactory = builder.build(reader, properties);
            }
        }
        catch(Exception e){
            e.printStackTrace();
        }
        finally{
            return _sqlSessionFactory;
        }
    }

    //根據酒店id獲取Mapper
    @SuppressWarnings("unchecked")
    public static <T> T getMapper(Integer id,Class<?> clazz){
        if(isConnection(id)){
            SqlSessionFactory sqlSessionFactory = dataSoruce.get(id);
            if(sqlSessionFactory==null){
                //建立資料工廠
                sqlSessionFactory = createSqlSessionFactory(id);
                if(sqlSessionFactory != null){
                    //排序演算法 刪除Map序列第一個元素,並將當前元素移至Map序列的首位
                    if(dataSoruce.size()>=MaxDataSourceSize){
                        removeFirstMap();
                    }
                    dataSoruce.put(id, sqlSessionFactory);
                }
                else
                    return null;
            }
            /*else{
                //排序演算法 將當前元素移至Map序列的首位
                dataSoruce.remove(id);
                dataSoruce.put(id, sqlSessionFactory);
            }*/
            SqlSession sqlSession = sqlSessionFactory.openSession();
            Object idal = sqlSession.getMapper(clazz);
            return (T)IDALProxy.bind(idal, sqlSession);
        }
        else
            return null;
    }

    //動態載入  SqlSession提交,釋放
    public static class IDALProxy implements InvocationHandler {
        private Object idal;
        private SqlSession sqlSession;

        private IDALProxy(Object idal, SqlSession sqlSession) {
            this.idal = idal;
            this.sqlSession = sqlSession;
        }

        public static Object bind(Object idal, SqlSession sqlSession) {
            return Proxy.newProxyInstance(idal.getClass().getClassLoader(),idal.getClass().getInterfaces(), new IDALProxy(idal, sqlSession));
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object object = null;
            try {
                object = method.invoke(idal, args);
            } catch(Exception e) {
                sqlSession.rollback();
                e.printStackTrace();

            } finally {
                sqlSession.commit();
                sqlSession.clearCache();
                sqlSession.close();
            }
            return object;
        }
    }

}

酒店工廠HotelFactory程式碼:

package factory;

import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;

import mapper.HotelMapper;

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 entity.Hotel;

public class HotelFactory {

    public static SqlSessionFactory sqlSessionFactory = null;
    //資料庫屬性值
    public static final String jdbc_properties="jdbc.properties";
    //資料庫配置檔案
    public final static String configuration = "configuration.xml";

    //建立本地酒店管理資料庫
    static{
        try {
            Properties properties = new Properties();
            InputStream in = Resources.getResourceAsStream(jdbc_properties);
            properties.load(in);
            String driver = properties.getProperty("jdbc.driverClassName");
            String url = properties.getProperty("jdbc.url");
            String username = properties.getProperty("jdbc.username");
            String password = properties.getProperty("jdbc.password");
            properties.setProperty("jdbc.driver",driver);  
            properties.setProperty("jdbc.url", url);  
            properties.setProperty("jdbc.username",username);  
            properties.setProperty("jdbc.password",password);
            Reader reader = Resources.getResourceAsReader(configuration);
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            sqlSessionFactory = builder.build(reader, properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //根據id獲取酒店實體
    @SuppressWarnings("finally")
    public static Hotel getHotelById(Integer id){
        Hotel hotel = null;
        SqlSession sqlSession = null;
        try{
            sqlSession = sqlSessionFactory.openSession();
            hotel = sqlSession.getMapper(HotelMapper.class).getHotelById(id);
        }
        catch(Exception e){
            e.printStackTrace();
        }
        finally{
            if(sqlSession != null)
                sqlSession.close();
            return hotel;
        }
    }
}

引用例項:

RoomsMapper roomMapper = getMapper(hotelid);
roomMapper.getRoom();

Hotel實體中存了資料庫的IP地址,埠號等基本資訊,根據hotel生成對應庫的Mapper例項進行資料操作。
通過IDALProxy類對生成Mapper例項的所有方法進行了再次封裝,實現資料庫資源的提交,快取清空和釋放。