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实例的所有方法进行了再次封装,实现数据库资源的提交,缓存清空和释放。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值