JFinal与JavaWeb对比总结

来源:互联网 发布:信信通是正规软件吗 编辑:程序博客网 时间:2024/06/06 13:07

事先准备:在mysql中创建两个数据库,一个叫javaweb_tes,一个叫jfinal_tes。两个数据库中都新建一个commodity表。

分别创建两个maven-archetype-webapp项目。将分别其命名为javaweb_maven与jfinal_maven。在Javaweb_maven项目中,通过maven引入servlet(servlet和jsp的使用)、junit(单元测试)、mysql(数据库引擎)、jstl(JSP标准标签库)和taglibs(JSP标准标签库)。

JFinal项目中,通过maven引入上面项目中所需要引入的所有外,需要额外引入jfinalJFinal的使用)

web.xml版本为3.0

使用JFinal前需要先创建一个继承JFinalConfig的类(以下简称Config类),然后开始配置web.xml文件,在其中加上

<filter><filter-name>jfinal</filter-name><filter-class>com.jfinal.core.JFinalFilter</filter-class><init-param><param-name>configClass</param-name><param-value>config.Config</param-value></init-param></filter><filter-mapping><filter-name>jfinal</filter-name><url-pattern>/*</url-pattern></filter-mapping>

其中<param-value></param-value>中填Config类的路径,<param-name></param-name>项中填继承Config类的名称。

一、连接数据库

事先准备:创建一个数据库配置文件:dbconfig.properties,里面写入数据库用到的各项配置,如:urlusernamepassworddriverClass。然后将该文件复制两份放入两个项目的src/main/resources中。(注:放入两个项目中的该配置文件中的url后面的数据库名是不同的,记得修改。)

例,其中jfinal_testdbconfig.properties文件代码:

driver=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:20001/jfinal_testuser=rootpassword=123456

1、JavaWeb

JavaWeb中,我建立了一个DBCon类,放在jdbc包下,用来连接数据库,获取连接完数据库的Connection对象(后续与数据库相关操作都是通过Connection对象进行)。

在该类中,我定义了4个私有静态变量,并定义了一个静态块用来从数据库配置文件中读取出来的各项值,然后赋值给那4个私有变量。

private static String DB_DRIVER;private static String DB_URL;private static String DB_USERNAME;private static String DB_PASSWORD;static{Properties p = new Properties();InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("dbconfig.properties");try {p.load(is);DB_DRIVER = p.getProperty("driver");DB_URL = p.getProperty("url");DB_USERNAME = p.getProperty("user");DB_PASSWORD = p.getProperty("password");} catch (IOException e) {e.printStackTrace();}}

在该类中,还定义了一个静态方法,用来获取Connection对象。

private static Connection connection;public static Connection GetConnection(){if (connection == null) {    try {            Class.forName(DB_DRIVER);        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        try {            connection = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);        } catch (SQLException e) {            e.printStackTrace();        }}return connection;}

之后其他类,就可以通过DBCon.GetConnection()来获取与数据库的连接对象了。

为了验证该方法是否正确,使用JUnit创建一个测试类DBConTest。里面添加一个测试方法testGetConnection,用来测试是否能成功获取数据库连接。

    @Test    public void testGetConnection() {        Connection conn = DBCon.GetConnection();        try {            assertNotNull(conn.createStatement().executeQuery("select * from commodity"));        } catch (SQLException e) {            e.printStackTrace();        }    }

测试通过,说明该方法可以成功获取数据库的连接。(数据库里已经有表格,没有数据也能测试通过)

2、JFinal

Config类中的configConstant方法中,使用loadPropertyFile方法获取数据库配置文件,这样方便之后再其他地方的调用。(me.setDevMode(true),是由于当前未开发版本,设置这个可以使得JFinal会对每次请求输出报告,如输出本次请求的URLControllerMethod以及请求所携带的参数。)

    @Override    public void configConstant(Constants me) {        loadPropertyFile("dbconfig.properties");        me.setDevMode(true);    }

设置完configConstant方法后,在Config类中的configPlugin方法里配置数据库的连接。

    @Override    public void configPlugin(Plugins me) {        DruidPlugin dp = new DruidPlugin(getProperty("url"), getProperty("user"), getProperty("password"), getProperty("driver"));        me.add(dp);        ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);        me.add(arp);        arp.addMapping("commodity", Commodity.class);        arp.addMapping("account", Account.class);    }

其中getProperty方法,读取的便是之前loadPropertyFile加载的配置文件中的相应键对应的值。如果存在重复的键值对,则取最后出现的键值对的值。

其中的arp.addMapping("commodity", Commodity.class)arp.addMapping("account", Account.class),是完成了数据库表格与项目model的映射。Commodity.classAccount.class是在项目中创建的继承自Model<Commodity>Model<Account>的类。

其中的DruidPlugin 类,这是一个数据源插件,可以使用jfinal自带的com.jfinal.plugin.druid.DruidPlugin类,不过需要在Maven中引入alibabadruid项目,然后通过调用其构造函数,输入urlusernamepassworddriver的值即可。除了使用jfinal自带的com.jfinal.plugin.druid.DruidPlugin类,还可以使用自定义的类,只需要实现com.jfinal.plugin.IPlugin接口和com.jfinal.plugin.activerecord.IDataSourceProvider接口即可,这样便无需在Maven中额外引入druid

接下来是ActiveRecordPlugin这个类。其定义了一个接受IDataSourceProvider类型的构造器:

public ActiveRecordPlugin(IDataSourceProvider dataSourceProvider) {      this(DbKit.MAIN_CONFIG_NAME, dataSourceProvider);  }  

this(DbKit.MAIN_CONFIG_NAME, dataSourceProvider)这段代码读取了一个重载的构造器,跟踪进去后发现,那个构造器又调用了一个新的重载的构造器。下面为新的重载的构造器:

public ActiveRecordPlugin(String configName, IDataSourceProvider dataSourceProvider, int transactionLevel) {if (StrKit.isBlank(configName)) {throw new IllegalArgumentException("configName can not be blank");}if (dataSourceProvider == null) {throw new IllegalArgumentException("dataSourceProvider can not be null");}this.dataSourceProvider = dataSourceProvider;this.config = new Config(configName, null, transactionLevel);}

此时,ActiveRecordPluginstatic变量的dataSourceProvider,就已经被赋为之前传入进来的实现了IDataSourceProvider接口的类型的实例了。

接下来看数据库到项目的映射的方法:

arp.addMapping("commodity", Commodity.class);

其中Commodity.class为:

public class Commodity extends Model<Commodity> {    private static final long serialVersionUID = -839562708492767863L;    public static final Commodity dao = new Commodity().dao();    }

其中声明的dao静态对象是为了方便查询操作而定义的,该对象并不是必须的。

回到ActiveRecordPlugin中,查看其是如何完成映射的。查看源码,我们可以发现,在ActiveRecordPlugin中,定义了两个addMapping方法:

public ActiveRecordPlugin addMapping(String tableName, String primaryKey, Class<? extends Model<?>> modelClass) {tableList.add(new Table(tableName, primaryKey, modelClass));return this;}public ActiveRecordPlugin addMapping(String tableName, Class<? extends Model<?>> modelClass) {tableList.add(new Table(tableName, modelClass));return this;}

一个的参数中需要primaryKey,一个的参数中不需要primaryKey。在继续进入Table类中,查看调用的两个构造器有什么不同。

进去后可以发现,有primaryKey参数的构造器中,比没有primaryKey参数的构造器多使用了一个setPrimaryKey(primaryKey.trim())方法。而setPrimaryKey的方法具体内容如下:

void setPrimaryKey(String primaryKey) {String[] arr = primaryKey.split(",");for (int i=0; i<arr.length; i++)arr[i] = arr[i].trim();this.primaryKey = arr;}

由此可知,是将传入的字符串,按,”分割,去除空格后,将这个数组赋值给primaryKey。如果没有传入primaryKey的情况下,primaryKey的值默认为id

因此通过addMapping方法来实现数据库表名到Model的映射关系的话,如果表的主键名为id的话,直接输入表名和相应的Model的类名即可;如果不是id的话,则需要手动指定。如果一个表有多个id的话,可以通过“,”分割开来。

回到源码中,在知道了如何使用数据库连接的情况下,再来看看具体的实现是如何进行的吧。

首先,我们将这个数据库连接是放在继承自JFinalConfig类的configPlugin方法中的。而这个Config类,我们是要在web.xml中配置的,如下。

<filter><filter-name>jfinal</filter-name><filter-class>com.jfinal.core.JFinalFilter</filter-class><init-param><param-name>configClass</param-name><param-value>config.Config</param-value></init-param></filter><filter-mapping><filter-name>jfinal</filter-name><url-pattern>/*</url-pattern></filter-mapping>

关于web.xml中的filter标签,是表示过滤器,其下方的filter-mapping标签中的<url-pattern>/*</url-pattern>则表明,通向该项目的任何请求,都会先转入该过滤器中,即必须先通过com.jfinal.core.JFinalFilter类。

JFinalFilter类继承自javax.servlet.Filter类。重写了init方法(Filter类的init方法会在服务器启动过程中调用),创建了Config的实例,并且调用了JFinalinit方法。具体代码如下:

private Handler handler;private String encoding;private JFinalConfig jfinalConfig;private Constants constants;private static final JFinal jfinal = JFinal.me();private static Log log;private int contextPathLength;public void init(FilterConfig filterConfig) throws ServletException {createJFinalConfig(filterConfig.getInitParameter("configClass"));if (jfinal.init(jfinalConfig, filterConfig.getServletContext()) == false) {throw new RuntimeException("JFinal init error!");}handler = jfinal.getHandler();constants = Config.getConstants();encoding = constants.getEncoding();jfinalConfig.afterJFinalStart();String contextPath = filterConfig.getServletContext().getContextPath();contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0 : contextPath.length());}

其中的jfinal.init(jfinalConfig, filterConfig.getServletContext(),调用了JFinalinit方法:

boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {this.servletContext = servletContext;this.contextPath = servletContext.getContextPath();initPathUtil();Config.configJFinal(jfinalConfig);// start plugin, init log factory and init engine in this methodconstants = Config.getConstants();initActionMapping();initHandler();initRender();initOreillyCos();initTokenManager();return true;}

其中的Config.configJFinal(jfinalConfig)是一个关键点,主要是通过Config来加载暴露给程序员的核心文件。点击去看看:

static void configJFinal(JFinalConfig jfinalConfig) {jfinalConfig.configConstant(constants); initLogFactory(); initEngine();jfinalConfig.configRoute(routes);jfinalConfig.configEngine(engine);jfinalConfig.configPlugin(plugins); startPlugins(); // very important!!!jfinalConfig.configInterceptor(interceptors);jfinalConfig.configHandler(handlers);}

注意其中的这一行:

jfinalConfig.configPlugin(plugins); startPlugins(); // very important!!!

这里的startPlugins();并不是注释,而是一个需要执行的方法。

而调用各个configxxxx方法的jfinalConfig对象,则是我们定义的Config对象的一个实例。因此这里的各个configxxxx方法,就是调用我们在Config类中定义的这几个方法。

在startPlugins这个方法中,就遍历了所有添加的Plugin,并分别调用了他们的start方法。这就是整个连接数据库的起始原点了。

而后回到ActiveRecordPlugin类中,查看其start()方法,在ActiveRecordPlugin的start()方法中,又调用了TableBuilder类的build方法。

在build方法中,遍历了添加进来的各个Table对象,并为其建表。并在其中调用了自身的doBuild方法,进行相关映射。源码如下:

private void doBuild(Table table, Connection conn, Config config) throws SQLException {table.setColumnTypeMap(config.containerFactory.getAttrsMap());if (table.getPrimaryKey() == null) {table.setPrimaryKey(config.dialect.getDefaultPrimaryKey());}String sql = config.dialect.forTableBuilderDoBuild(table.getName());Statement stm = conn.createStatement();ResultSet rs = stm.executeQuery(sql);ResultSetMetaData rsmd = rs.getMetaData();for (int i=1; i<=rsmd.getColumnCount(); i++) {String colName = rsmd.getColumnName(i);String colClassName = rsmd.getColumnClassName(i);Class<?> clazz = javaType.getType(colClassName);if (clazz != null) {table.setColumnType(colName, clazz);}else {int type = rsmd.getColumnType(i);if (type == Types.BINARY || type == Types.VARBINARY || type == Types.BLOB) {table.setColumnType(colName, byte[].class);} else if (type == Types.CLOB || type == Types.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();}

3、小结

使用方式:

JavaWeb

需要自己去从头开始写各个部分的代码,读取数据库配置文件,加载数据库驱动,获取数据库连接,之后有操作数据库操作的时候,再来调用这个获取的连接对象。

JFinal

创建一个继承自JFinalConfig的类,并将其配置在web.xml中的jfinal过滤器中的初始化参数中(这是JFina初始化的操作,没有这一步JFinal将无法调用)。

之后在创建的Config类中的configPlugin方法里进行配置即可,示例代码如下:

    @Override    public void configPlugin(Plugins me) { loadPropertyFile("dbconfig.properties");        DruidPlugin dp = new DruidPlugin(getProperty("url"), getProperty("user"), getProperty("password"), getProperty("driver"));        me.add(dp);        ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);        me.add(arp);        arp.addMapping("commodity", Commodity.class);        arp.addMapping("account", Account.class);    }


loadPropertyFile方法为读取相应的配置文件,之后可以通过getProperty方法取出配置文件中定义的各项值出来。

DruidPlugin 为数据库连接池插件,JFinal自带,不过需要引入alibabadruid类库才可以使用。不过也可以不使用DruidPlugin,而使用一个自定义的类,不过需要实现IPluginIDataSourceProvider这两个接口。

me.add(IPlugin plugin)方法,添加一个实现了IPlugin接口的类型的对象进入pluginList中,而在项目部署过程中,会遍历pluginList中的所有对象,调用其start()方法。

ActiveRecordPlugin类为初始化数据库表格到项目Model的映射,使用arp.addMapping方法,传入“表格名”和“Model类名”,即可完成映射,不过这样完成的映射,表格的主键必须为id,如果表格的主键不是id,可以通过arp.addMapping(String tableName, String primaryKey, Class<? extends Model<?>> modelClass),即在添加一个String类型的参数位于第二的位置表示主键名即可。

JFinal启动类图示意:

 

 
原创粉丝点击