1 数据库插件配置
public void configPlugin(Plugins me) { C3p0Plugin cp = new C3p0Plugin("jdbc:mysql://localhost/db_name", "userName", "password");//1.1 配置c3p0插件 me.add(cp);//1.2添加c3p0到plugins中 ActiveRecordPlugin arp = new ActiveRecordPlugin(cp);//1.3 创建ActiveRecordPlugin me.add(arp); arp.addMapping("user", User.class);//添加tablesName 到Model的映射 arp.addMapping("article", "article_id", Article.class); } }
1.1 c3p0类
public class C3p0Plugin implements IPlugin, IDataSourceProvider { private String jdbcUrl; private String user; private String password; private String driverClass = "com.mysql.jdbc.Driver"; private int maxPoolSize = 100; private int minPoolSize = 10; private int initialPoolSize = 10; private int maxIdleTime = 20; private int acquireIncrement = 2; private ComboPooledDataSource dataSource; public C3p0Plugin setDriverClass(String driverClass) { if (StrKit.isBlank(driverClass)) throw new IllegalArgumentException("driverClass can not be blank."); this.driverClass = driverClass; return this; } public C3p0Plugin setMaxPoolSize(int maxPoolSize) { if (maxPoolSize < 1) throw new IllegalArgumentException("maxPoolSize must more than 0."); this.maxPoolSize = maxPoolSize; return this; } public C3p0Plugin setMinPoolSize(int minPoolSize) { if (minPoolSize < 1) throw new IllegalArgumentException("minPoolSize must more than 0."); this.minPoolSize = minPoolSize; return this; } public C3p0Plugin setInitialPoolSize(int initialPoolSize) { if (initialPoolSize < 1) throw new IllegalArgumentException("initialPoolSize must more than 0."); this.initialPoolSize = initialPoolSize; return this; } public C3p0Plugin setMaxIdleTime(int maxIdleTime) { if (maxIdleTime < 1) throw new IllegalArgumentException("maxIdleTime must more than 0."); this.maxIdleTime = maxIdleTime; return this; } public C3p0Plugin setAcquireIncrement(int acquireIncrement) { if (acquireIncrement < 1) throw new IllegalArgumentException("acquireIncrement must more than 0."); this.acquireIncrement = acquireIncrement; return this; } public C3p0Plugin(String jdbcUrl, String user, String password) {//1.1 调用的构造函数 this.jdbcUrl = jdbcUrl; this.user = user; this.password = password; } public C3p0Plugin(String jdbcUrl, String user, String password, String driverClass) { this.jdbcUrl = jdbcUrl; this.user = user; this.password = password; this.driverClass = driverClass != null ? driverClass : this.driverClass; } public C3p0Plugin(String jdbcUrl, String user, String password, String driverClass, Integer maxPoolSize, Integer minPoolSize, Integer initialPoolSize, Integer maxIdleTime, Integer acquireIncrement) { initC3p0Properties(jdbcUrl, user, password, driverClass, maxPoolSize, minPoolSize, initialPoolSize, maxIdleTime, acquireIncrement); } private void initC3p0Properties(String jdbcUrl, String user, String password, String driverClass, Integer maxPoolSize, Integer minPoolSize, Integer initialPoolSize, Integer maxIdleTime, Integer acquireIncrement) { this.jdbcUrl = jdbcUrl; this.user = user; this.password = password; this.driverClass = driverClass != null ? driverClass : this.driverClass; this.maxPoolSize = maxPoolSize != null ? maxPoolSize : this.maxPoolSize; this.minPoolSize = minPoolSize != null ? minPoolSize : this.minPoolSize; this.initialPoolSize = initialPoolSize != null ? initialPoolSize : this.initialPoolSize; this.maxIdleTime = maxIdleTime != null ? maxIdleTime : this.maxIdleTime; this.acquireIncrement = acquireIncrement != null ? acquireIncrement : this.acquireIncrement; } public C3p0Plugin(File propertyfile) { FileInputStream fis = null; try { fis = new FileInputStream(propertyfile); Properties ps = new Properties(); ps.load(fis); initC3p0Properties(ps.getProperty("jdbcUrl"), ps.getProperty("user"), ps.getProperty("password"), ps.getProperty("driverClass"), toInt(ps.getProperty("maxPoolSize")), toInt(ps.getProperty("minPoolSize")), toInt(ps.getProperty("initialPoolSize")), toInt(ps.getProperty("maxIdleTime")),toInt(ps.getProperty("acquireIncrement"))); } catch (Exception e) { e.printStackTrace(); } finally { if (fis != null) try {fis.close();} catch (IOException e) {e.printStackTrace();} } } public C3p0Plugin(Properties properties) { Properties ps = properties; initC3p0Properties(ps.getProperty("jdbcUrl"), ps.getProperty("user"), ps.getProperty("password"), ps.getProperty("driverClass"), toInt(ps.getProperty("maxPoolSize")), toInt(ps.getProperty("minPoolSize")), toInt(ps.getProperty("initialPoolSize")), toInt(ps.getProperty("maxIdleTime")),toInt(ps.getProperty("acquireIncrement"))); } //c3p0初始化,初始化datasource public boolean start() { dataSource = new ComboPooledDataSource(); dataSource.setJdbcUrl(jdbcUrl); dataSource.setUser(user); dataSource.setPassword(password); try {dataSource.setDriverClass(driverClass);} catch (PropertyVetoException e) {dataSource = null; System.err.println("C3p0Plugin start error"); throw new RuntimeException(e);} dataSource.setMaxPoolSize(maxPoolSize); dataSource.setMinPoolSize(minPoolSize); dataSource.setInitialPoolSize(initialPoolSize); dataSource.setMaxIdleTime(maxIdleTime); dataSource.setAcquireIncrement(acquireIncrement); return true; } private Integer toInt(String str) { return Integer.parseInt(str); } public DataSource getDataSource() { return dataSource; } public boolean stop() { if (dataSource != null) dataSource.close(); return true; } }
可以看到c3p0实现了Iplugin 和IDatasoureProvider。
1.3 AtiveRecordPlugin 类中构造方法的具体实现
//其中c3p0为IDataSourceProvider的子类 public ActiveRecordPlugin(IDataSourceProvider dataSourceProvider) { this(DbKit.MAIN_CONFIG_NAME, dataSourceProvider); }
1.4 arp.addMapping()的具体实现
public ActiveRecordPlugin addMapping(String tableName, Class<? extends Model<?>> modelClass) { tableList.add(new Table(tableName, modelClass));// 添加class类与name的映射 return this; }
其中table 类保存了元数据包含列名和类型。
JFinal初始化时调用了plugins.start();
其中ActiveRecordPlugin的start方法实现如下:
public boolean start() { if (isStarted)//isStarted是启动标识防止重复启动 return true; if (dataSourceProvider != null) dataSource = dataSourceProvider.getDataSource();//获取dataSource if (dataSource == null) throw new RuntimeException("ActiveRecord start error: ActiveRecordPlugin need DataSource or DataSourceProvider"); //生成Config设置或者使用已有的设置 if (config == null) config = new Config(configName, dataSource, dialect, showSql, devMode, transactionLevel, containerFactory, cache); DbKit.addConfig(config); //建立config与数据库的关联 核心与关键 TableBuilder.build(tableList, config); Db.init(); isStarted = true; return true; }
build方法实现如下:
static void build(List<Table> tableList, Config config) { Table temp = null; Connection conn = null; try { conn = config.dataSource.getConnection();//获取数据库连接 TableMapping tableMapping = TableMapping.me();//获取TableMapping 单例 //tableList中的数据为arp.add("User",User.class); for (Table table : tableList) { temp = table; doBuild(table, conn, config);//建立每一个表与Model的对应关系 tableMapping.putTable(table);//将构建成功的每一个table添加到TableMapping中 DbKit.addModelToConfigMapping(table.getModelClass(), config); } } catch (Exception e) { if (temp != null) System.err.println("Can not create Table object, maybe the table " + temp.getName() + " is not exists."); throw new ActiveRecordException(e); } finally { config.close(conn); } }
doBuild()的实现方法如下:
private static void doBuild(Table table, Connection conn, Config config) throws SQLException { table.setColumnTypeMap(config.containerFactory.getAttrsMap()); if (table.getPrimaryKey() == null)//table的主键是否为空 table.setPrimaryKey(config.dialect.getDefaultPrimaryKey());//将id设置为table的主键 String sql = config.dialect.forTableBuilderDoBuild(table.getName());//"select * from "+tablename+" where 1=2" Statement stm = conn.createStatement(); ResultSet rs = stm.executeQuery(sql); ResultSetMetaData rsmd = rs.getMetaData();//获取metadata for (int i=1; i<=rsmd.getColumnCount(); i++) { String colName = rsmd.getColumnName(i);//获取列名 String colClassName = rsmd.getColumnClassName(i);//获取the fully-qualified name Class<?> clazz = strToType.get(colClassName); if (clazz != null) {//判断是否为class类 table.setColumnType(colName, clazz); } else { int type = rsmd.getColumnType(i);//获取字段类型 if (type == Types.BLOB) {//blob table.setColumnType(colName, byte[].class); } else if (type == Types.CLOB || type == Types.NCLOB) {//Clob||NCLOB类型 table.setColumnType(colName, String.class); } else { table.setColumnType(colName, String.class); } // core.TypeConverter // throw new RuntimeException("You've got new type to mapping. Please add code in " + TableBuilder.class.getName() + ". The ColumnClassName can't be mapped: " + colClassName); } } rs.close(); stm.close(); }
巧妙地利用“select * from tablename where 1=2"的语句获取table的相关信息,实现数据库到model的精巧映射。