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