JFinal与JavaWeb对比总结

本文对比了JavaWeb和JFinal在连接数据库上的差异。在JavaWeb中,需要手动编写代码来读取数据库配置、加载驱动、获取连接。而在JFinal中,只需配置JFinalConfig,通过ActiveRecordPlugin和DruidPlugin自动完成数据库连接和映射。JFinal的配置更简洁,自动化程度更高。
摘要由CSDN通过智能技术生成

事先准备:在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.Driver
url=jdbc:mysql://localhost:20001/jfinal_test
user=root
password=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 method
	constants = 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启动类图示意:

 


                
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值