package org.nutz.dao.util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import javax.sql.DataSource; import org.nutz.dao.Dao; import org.nutz.dao.impl.NutDao; import org.nutz.dao.impl.SimpleDataSource; import org.nutz.lang.Files; import org.nutz.lang.Lang; import org.nutz.lang.Mirror; import org.nutz.lang.Streams; import org.nutz.log.Log; import org.nutz.log.Logs; /** * 为非Mvc,Ioc环境下的程序提供辅助支持.<p/> * <b>DaoUp不是一次性产品!! 如果新建DaoUp然后立马抛弃掉, 从中获取的NutDao/DataSource将会关闭!!</b><p/> * <b>请注意使用场景!!! 在Mvc下有IocBy的情况下,不需要也不应该使用本类!!</b><p/> * <b>Mvc下可以通过 Mvcs.getIoc()或Mvcs.ctx().getDefaultIoc()获取Ioc容器,从而获取其中的Dao实例!!</b><p/> * <b>Mvc应尽量使用注入,而非主动取Dao实例,更不应该主动new NutDao!!!</b> * <p/> 最基本的用法<p/> <code> DaoUp.me().init(new File("db.properties")); Dao dao = DaoUp.me().dao(); dao.insert(.......); // 注意,不是每次用完Dao就关,是整个程序关闭的时候才关!! // 程序结束前关闭相关资源. DaoUp.me().close(); </code> <p/><p/> 请参阅test源码中的DaoUpTest获取Dao的入门技巧. * * @author wendal(wendal1985@gmail.com) * */ public class DaoUp { /** * 日志对象 */ private static final Log log = Logs.get(); /** * 内置单例 */ protected static DaoUp me = new DaoUp("_defult_"); /** * Druid数据源的工厂方法类 */ protected static Class<?> druidFactoryClass; /** * 如果本对象被GC,是否触发自动关闭 */ protected boolean autoCloseWhenFinalize = true; static { try { /** * 加载DruidDataSourceFactory, 即Druid连接池的工厂类 */ druidFactoryClass = Lang.loadClass("com.alibaba.druid.pool.DruidDataSourceFactory"); } catch (ClassNotFoundException e) { // 找不到就用内置的SimpleDataSource好了. // TODO 支持其他类型的数据源, 低优先级 } } /** * 获取内置的DaoUp单例 * @return DaoUp实例 */ public static DaoUp me() { return me; } /** * 需要新建多个DaoUp,请继承DaoUp,从而暴露构造方法或使用工厂方法!! */ protected DaoUp(String name) { this.name = name; } /** * Dao对象 */ protected Dao dao; /** * 连接池 */ protected DataSource dataSource; /** * 当前DaoUp的名称 */ protected String name; /** * 返回所持有的Dao实例,如果DaoUp还没初始化或已经关闭,这里会返回null * @return Dao实例 */ public Dao dao() { return dao; } /** * 获取数据源, 如果DaoUp还没初始化或已经关闭,这里会返回null * @return 数据源(连接池) */ public DataSource getDataSource() { return dataSource; } /** * 主动设置数据源(连接池) * @param dataSource 数据源(连接池) */ public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; setDao(new NutDao(dataSource)); } /** * 主动设置Dao实例 * @param dao Dao实例 */ public void setDao(Dao dao) { if (this.dao != null) log.infof("override old Dao=%s by new Dao=%s", this.dao, dao); this.dao = dao; } /** * 从classpath或当前目录下查找配置文件来进行初始化 * @param name */ public void init(String name) throws IOException { init(new FileInputStream(Files.findFile(name))); } /** * 从一个文件读取数据库配置 * @param f 配置文件 * @throws IOException 文件不可读取时抛出异常 */ public void init(File f) throws IOException { init(new FileInputStream(f)); } /** * 从一个流读取数据库配置 * @param in 输入流,包含配置信息 * @throws IOException 读取失败是抛出异常 */ public void init(InputStream in) throws IOException { Properties props = new Properties(); try { props.load(in); init(props); } finally { Streams.safeClose(in); } } /** * 给定一个Properties配置,不能为null!!!! 最起码要包含一个叫url的参数!!! * @param props 配置信息 */ public void init(Properties props) { if (dao != null) { throw new IllegalArgumentException("DaoUp is inited!!"); } if (props.size() == 0) { throw new IllegalArgumentException("DaoUp props size=0!!!"); } DataSource ds = buildDataSource(props); setDataSource(ds); } /** * 构建DataSource,子类可覆盖. 如果存在Druid,则使用之,否则使用内置的SimpleDataSource * @param props 配置信息 * @return 目标DataSource */ protected DataSource buildDataSource(Properties props) { if (druidFactoryClass != null) { log.debug("build DruidDataSource by props"); Mirror<?> mirror = Mirror.me(druidFactoryClass); DataSource ds = (DataSource) mirror.invoke(null, "createDataSource", props); if (!props.containsKey("maxWait")) Mirror.me(ds).setValue(ds, "maxWait", 15*1000); return ds; } log.debug("build SimpleteDataSource by props"); return SimpleDataSource.createDataSource(props); } /** * 关闭本DaoUp,将关闭DataSource并将dao和dataSource置为null!!!<p/> * <b>只能在程序关闭时调用,严禁在每次Dao操作后调用!!</b> */ public synchronized void close() { if (dao == null) return; log.infof("shutdown DaoUp(name=%s)", name); try { Mirror.me(dataSource).invoke(dataSource, "close"); } catch (Throwable e) { } this.dataSource = null; this.dao = null; } /** * 设置是否在本对象被GC时自动关闭相关资源.<p/> * <b>若要设置为false, 请慎重考虑,因为绝大部分情况下设置为true并不能解决您当前遇到的问题!!</b><p/> * DaoUp类不是设计为即用即抛的!!!而是设计为单例模式的!!!!!!!<p/> * <b>如果是遇到DataSource is closed之类的异常, 在考虑使用本配置前请先检讨代码!!!</b><p/> * @param autoCloseWhenFinalize 是否自动关闭资源 */ public void setAutoCloseWhenFinalize(boolean autoCloseWhenFinalize) { this.autoCloseWhenFinalize = autoCloseWhenFinalize; if (!autoCloseWhenFinalize) { log.warnf("DaoUp[%s] autoCloseWhenFinalize is disabled. You had been WARN!!", name); } } /** * 如果被GC,主动触发关闭,除非autoCloseWhenFinalize为false */ protected void finalize() throws Throwable { if (autoCloseWhenFinalize) close(); super.finalize(); } // /** // * 提供一个配置对象,然后生成Dao实例<p/> // * <p/>应该把对象 // * <b>返回的对象!!</b> // * @param conf 可以为DataSource/File/InputStream/Properties/String // * @return 初始化好的Dao实例 // * @throws IOException 读取文件出错时抛出 // */ // public static Dao factory(Object conf) throws IOException { // if (conf == null) // return null; // if (conf instanceof Dao) // return (Dao) conf; // if (conf instanceof DataSource) // return new NutDao((DataSource)conf); // DaoUp up = new DaoUp("daoup_factory_" + System.currentTimeMillis()); // if (conf instanceof File) { // up.init((File)conf); // } else if (conf instanceof InputStream) { // up.init((InputStream)conf); // } else if (conf instanceof Properties) { // up.init((Properties)conf); // } else { // up.init(conf.toString()); // } // Dao dao = up.dao(); // up.autoCloseWhenFinalize = false; // return dao; // } // TODO 完成一个repl // public static void main(String[] args) { // // } }