JSRUN 用代码说话

ActiveRecord

编辑教程

ActiveRecord

ActiveRecord 是 JFinal 最核心的组成部分之一,通过 ActiveRecord 来操作数据库,将极大 地减少代码量,极大地提升开发效率。

ActiveRecordPlugin

ActiveRecord 是作为 JFinal 的 Plugin 而存在的,所以使用时需要在 JFinalConfig 中配置ActiveRecordPlugin。 以下是 Plugin 配置示例代码:

public class DemoConfig extends JFinalConfig {
    public void configPlugin(Plugins me) {
    C3p0Plugin cp = new C3p0Plugin("jdbc:mysql://localhost/db_name", "userName", "password");
    me.add(cp);
    ActiveRecordPlugin arp = new ActiveRecordPlugin(cp); me.add(arp);
    arp.addMapping("user", User.class); arp.addMapping("article", "article_id", Article.class);
    }
}

以上代码配置了两个插件:C3p0Plugin 与 ActiveRecordPlugin,前者是 c3p0 数据源插件, 后者是 ActiveRecrod 支持插件。ActiveReceord 中定义了 addMapping(String tableName, Class<? extends Model> modelClass>)方法,该方法建立了数据库表名到 Model 的映射关系。

另外,以上代码中 arp.addMapping(“user”, User.class),表的主键名为默认为“id”,如果主 键名称为 “user_id”则需要手动指定,如:arp.addMapping(“user”, “user_id”, User.class)。

Model

Model 是 ActiveRecord 中最重要的组件之一,它充当 MVC 模式中的 Model 部分。

以下Model 定义示例代码:

public class User extends Model<User> {
    public static final User dao = new User();
}

以上代码中的 User 通过继承 Model,便立即拥有的众多方便的操作数据库的方法。在 User 中声明的 dao 静态对象是为了方便查询操作而定义的,该对象并不是必须的。

基于 ActiveRecord 的 Model 无需定义属性,无需定义 getter、setter 方法,无需 XML 配置,无需 Annotation 配置, 极大降低了代码量。 以下为 Model 的一些常见用法:

// 创建name属性为James,age属性为25的User对象并添加到数据库
new User().set("name", "James").set("age", 25).save();

// 删除id值为25的User User.dao.deleteById(25);

// 查询id值为25的User将其name属性改为James并更新到数据库
User.dao.findByIdLoadColumns (25).set("name", "James").update();

// 查询id值为25的user, 且仅仅取name与age两个字段的值
User user = User.dao.findByIdLoadColumns (25, "name, age");

// 获取user的name属性
String userName = user.getStr("name");

// 获取user的age属性
Integer userAge = user.getInt("age");

// 查询所有年龄大于18岁的user
List<User> users = User.dao.find("select * from user where age>18");

// 分页查询年龄大于18的user,当前页号为1,每页10个user
Page<User> userPage = User.dao.paginate(1, 10, "select *", "from user where age > ?", 18);

特别注意:User 中定义的 public static final User dao 对象是全局共享的,只能用于数据库查询, 不能用于数据承载对象。数据承载需要使用 new User().set(…)来实现。

JavaBean 与 Model 合体

JFinal 2.1 版本提供了 ModelGenerator 、 BaseModelGenerator 、 MappingKitGernator 、 DataDictionaryGenerator,分别生成 Model、BaseModel、MappingKit、DataDictionary 四类文件。 可根据数据表自动化生成这四类文件。

相对于 JFinal 2.1 之前的版本,生成后的 Model 继承自 BaseModel 而非继承自 Model, BaseModel 中拥有 getter、setter 方法遵守传统 java bean 规范,Model 继承自 BaseModel 即完成 了 JavaBean 与 Model 合体,拥有了传统 JavaBean 所有的优势,并且所有的 getter、setter 方法 完全无需人工干预,数据表有任何变动一键重新生成即可。

独创 Db + Record 模式

Db 类及其配套的 Record 类,提供了在 Model 类之外更为丰富的数据库操作功能。使用 Db 与 Record 类时,无需对数据库表进行映射,Record 相当于一个通用的 Model。以下为 Db + Record 模式的一些常见用法:

// 创建name属性为James,age属性为25的record对象并添加到数据库
Record user = new Record().set("name", "James").set("age", 25); Db.save("user", user);

// 删除id值为25的user表中的记录
Db.deleteById("user", 25);

// 查询id值为25的Record将其name属性改为James并更新到数据库 user = Db.findById("user", 25).set("name", "James"); Db.update("user", user);

// 获取user的name属性
String userName = user.getStr("name");
// 获取user的age属性
Integer userAge = user.getInt("age");

// 查询所有年龄大于18岁的user
List<Record> users = Db.find("select * from user where age > 18");

// 分页查询年龄大于18的user,当前页号为1,每页10个user

Page<Record> userPage = Db.paginate(1, 10, "select *", "from user where age > ?", 18); 以下为事务处理示例:

boolean succeed = Db.tx(new IAtom(){
    public boolean run() throws SQLException {
        int count = Db.update("update account set cash = cash - ? where id = ?", 100, 123);
    int count2 = Db.update("update account set cash = cash + ? where id = ?", 100, 456);
        return count == 1 && count2 == 1;
}});

以上两次数据库更新操作在一个事务中执行,如果执行过程中发生异常或者 invoke()方法 返回 false,则自动回滚事务。

声明式事务

ActiveRecord 支持声名式事务,声明式事务需要使用 ActiveRecordPlugin 提供的拦截器来 实现,拦截器的配置方法见 Interceptor 有关章节。以下代码是声明式事务示例:

// 本例仅为示例, 并未严格考虑账户状态等业务逻辑
@Before(Tx.class)
public void trans_demo() {
    // 获取转账金额
    Integer transAmount = getParaToInt("transAmount");
    // 获取转出账户id
    Integer fromAccountId = getParaToInt("fromAccountId");
    // 获取转入账户id
    Integer toAccountId = getParaToInt("toAccountId");
    // 转出操作
    Db.update("update account set cash = cash - ? where id = ?", transAmount, fromAccountId);
    // 转入操作
    Db.update("update account set cash = cash + ? where id = ?", transAmount, toAccountId);
}

以上代码中,仅声明了一个 Tx 拦截器即为 action 添加了事务支持。除此之外 ActiveRecord 还配备了 TxByActionKeys、TxByActionKeyRegex、TxByMethods、TxByMethodRegex,分别 支持 actionKeys、actionKey 正则、actionMethods、actionMethod 正则声明式事务,以下是示例代码:

public void configInterceptor(Interceptors me) { me.add(new TxByMethodRegex("(.*save.*|.*update.*)")); me.add(new TxByMethods("save", "update"));

me.add(new TxByActionKeyRegex("/trans.*")); me.add(new TxByActionKeys("/tx/save", "/tx/update"));

上例中的 TxByRegex 拦截器可通过传入正则表达式对 action 进行拦截,当 actionKey 被正 则匹配上将开启事务。

TxByActionKeys 可以对指定的 actionKey 进行拦截并开启事务, TxByMethods 可以对指定的 method 进行拦截并开启事务。

注意:MySql 数据库表必须设置为 InnoDB 引擎时才支持事务,MyISAM 并不支持事务。

Cache

ActiveRecord 可以使用缓存以大大提高性能,以下代码是 Cache 使用示例:

public void list() {
    List<Blog> blogList = Blog.dao.findByCache("cacheName", "key", "select * from blog");
    setAttr("blogList", blogList).render("list.html");
}

上例 findByCache 方 法 中 的 cacheName 需 要 在 ehcache.xml 中配置 如:

此 外 Model.paginateByCache(…) 、 Db.findByCache(…) 、 Db.paginateByCache(…)方法都提供了 cache 支持。

在使用时,只需传入 cacheName、key 以及 在 ehccache.xml 中配置相对应的 cacheName 就可以了。

Dialect 多数据库支持

目前 ActiveRecordPlugin 提供了 MysqlDialect、OracleDialect、AnsiSqlDialect 实现类。

MysqlDialect 与 OracleDialect 分别实现对 Mysql 与 Oracle 的支持,AnsiSqlDialect 实现对遵守 ANSI SQL 数据库的支持。以下是数据库 Dialect 的配置代码:

public class DemoConfig extends JFinalConfig {
    public void configPlugin(Plugins me) { ActiveRecordPlugin arp = new ActiveRecordPlugin(…); me.add(arp);
    // 配置Postgresql方言
    arp.setDialect(new PostgresqlDialect());
    }
}

表关联操作

JFinal ActiveRecord 天然支持表关联操作,并不需要学习新的东西,此为无招胜有招。表 关联操作主要有两种方式:一是直接使用 sql 得到关联数据;二是在 Model 中添加获取关联数据的方法。

假定现有两张数据库表:user、blog,并且 user 到 blog 是一对多关系,blog 表中使用 user_id关联到 user 表。如下代码演示使用第一种方式得到 user_name:

public void relation() {
    String sql = "select b.*, u.user_name from blog b inner join user u on b.user_id=u.id where b.id=?";
    Blog blog = Blog.dao.findFirst(sql, 123); String name = blog.getStr("user_name");
}

以下代码演示第二种方式在 Blog 中获取相关联的 User 以及在 User 中获取相关联的Blog:

public class Blog extends Model<Blog>{
    public static final Blog dao = new Blog();

    public User getUser() {
        return User.dao.findById(get("user_id"));
}
}
public class User extends Model<User>{
    public static final User dao = new User();

    public List<Blog> getBlogs() {
    return Blog.dao.find("select * from blog where user_id=?", get("id"));
    }
}

复合主键

JFinal ActiveRecord 从 2.0 版本开始,采用极简设计支持复合主键,对于 Model 来说需要 在映射时指定复合主键名称,以下是具体例子:

ActiveRecordPlugin arp = new ActiveRecordPlugin(c3p0Plugin);
// 多数据源的配置仅仅是如下第二个参数指定一次复合主键名称
arp.addMapping("user_role", "userId, roleId", UserRole.class);

//同时指定复合主键值即可查找记录 UserRole.dao.findById(123, 456);

//同时指定复合主键值即可删除记录 UserRole.dao.deleteById(123, 456);

如上代码所示,对于 Model 来说,只需要在添加 Model 映射时指定复合主键名称即可开 始使用复合主键,在后续的操作中 JFinal 会对复合主键支持的个数进行检测,当复合主键数量 不正确时会报异常,尤其是复合主键数量不够时能够确保数据安全。复合主键不限定只能有两 个,可以是数据库支持下的任意多个。

对于 Db + Record 模式来说,复合主键的使用不需要配置,直接用即可:

Db.findById("user_role", "roleId, userId", 123, 456);
Db.deleteById("user_role", "roleId, userId", 123, 456);

Oracle 支持

Oracle 数据库具有一定的特殊性,JFinal 针对这些特殊性进行了一些额外的支持以方便广 大的 Oracle 使用者。以下是一个完整的 Oracle 配置示例:

public class DemoConfig extends JFinalConfig {
    public void configPlugin(Plugins me) { C3p0Plugin cp = new C3p0Plugin(……);
    //配置Oracle驱动
    cp. setDriverClass("oracle.jdbc.driver.OracleDriver"); me.add(cp);
    ActiveRecordPlugin arp = new ActiveRecordPlugin(cp); me.add(arp);
    // 配置Oracle方言
    arp.setDialect(new OracleDialect());
    //  配置属性名(字段名)大小写不敏感容器工厂 arp.setContainerFactory(new CaseInsensitiveContainerFactory()); arp.addMapping("user", "user_id", User.class);
}

由于 Oracle 数据库会自动将属性名(字段名)转换成大写,所以需要手动指定主键名为大写, 如:arp.addMaping(“user”, “ID”, User.class)。

如果想让 ActiveRecord 对属性名(字段名)的大小写不敏感可以通过设置CaseInsensitiveContainerFactory 来达到 ,有了这个设置, 则 arp.addMaping(“user”, “ID”, User.class)不再需要了。

另外,Oracle 并未直接支持自增主键,JFinal 为此提供了便捷的解决方案。要让 Oracle 支 持自动主键主要分为两步:一是创建序列,二是在 model 中使用这个序列,具体办法如下:

1:通过如下办法创建序列,本例中序列名为:MY_SEQ

CREATE SEQUENCE MY_SEQ INCREMENT BY 1
MINVALUE 1
MAXVALUE 9999999999999999
START WITH 1
CACHE 20;

2:在 YourModel.set(…)中使用上面创建的序列

// 创建User并使用序列
User user = new User().set("id", "MY_SEQ.nextval").set("age", 18); user.save();
// 获取id值
Integer id = user.get("id");

序列的使用很简单,只需要 yourModel.set(主键名, 序列名 + “.nextval”)就可以了。特别注意这里的 “.nextval” 后缀一定要是小写,OracleDialect 对该值的大小写敏感。

多数据源支持

ActiveRecordPlugin 可同时支持多数据源、多方言、多缓存、多事务级别等特性,对每个 ActiveRecordPlugin 可进行彼此独立的配置。简言之 JFinal 可以同时使用多数据源,并且可 以针对这多个数据源配置独立的方言、缓存、事务级别等。

当使用多数据源时,只需要对每个 ActiveRecordPlugin 指定一个 configName 即可,如下是代码示例:

public void configPlugin(Plugins me) {
// mysql 数据源
C3p0Plugin dsMysql = new C3p0Plugin(…); me.add(dsMysql);

// mysql ActiveRecrodPlugin 实例,并指定configName为 mysql ActiveRecordPlugin arpMysql = new ActiveRecordPlugin("mysql", dsMysql); me.add(arpMysql);
arpMysql.setCache(new EhCache()); arpMysql.addMapping("user", User.class);

// oracle 数据源
C3p0Plugin dsOracle = new C3p0Plugin(…); me.add(dsOracle);

// oracle ActiveRecrodPlugin 实例,并指定configName为 oracle ActiveRecordPlugin arpOracle = new ActiveRecordPlugin("oracle", dsOracle); me.add(arpOracle);
arpOracle.setDialect(new OracleDialect()); arpOracle.setTransactionLevel(8); arpOracle.addMapping("blog", Blog.class);
}

以上代码创建了创了两个 ActiveRecordPlugin 实例 arpMysql 与 arpOrace,特别注意创建实 例的同时指定其 configName 分别为 mysql 与 oracle。arpMysql 与 arpOracle 分别映射了不同的Model,配置了不同的方言。

对于 Model 的使用,不同的 Model 会自动找到其所属的 ActiveRecrodPlugin 实例以及相关 配置进行数据库操作。假如希望同一个 Model 能够切换到不同的数据源上使用,也极度方便, 这种用法非常适合不同数据源中的 table 拥有相同表结构的情况,开发者希望用同一个 Model 来操作这些相同表结构的 table,以下是示例代码:

public void multiDsModel() {
    // 默认使用arp.addMapping(...)时关联起来的数据源
    Blog blog = Blog.me.findById(123);

    // 只需调用一次use方法即可切换到另一数据源上去
    blog.use("backupDatabase").save();
}

上例中的代码,blog.use(“backupDatabase”)方法切换数据源到 backupDatabase 并直接将数 据保存起来。

特别注意:只有在同一个 Model 希望对应到多个数据源的 table 时才需要使用 use 方法,如果 同一个 Model 唯一对应一个数据源的一个 table,那么数据源的切换是自动的,无需使用 use 方法。

对于 Db + Record 的使用,数据源的切换需要使用 Db.use(cnfigName)方法得到数据库操作 对象,然后就可以进行数据库操作了,以下是代码示例:

// 查询 dsMysql数据源中的 user
List<Record> users = Db.use("mysql").find("select * from user");
// 查询 dsOracle数据源中的 blog
List<Record> blogs = Db.use("oracle").find("select * from blog");

以上两行代码,分别通过 configName 为 mysql、oracle 得到各自的数据库操作对象,然后 就可以如同单数据完全一样的方式来使用数据库操作 API 了。

简言之,对于 Db + Record 来 说,多数据源相比单数据源仅需多调用一下 Db.use(configName),随后的 API 使用方式完全一 样。

注意最先创建的 ActiveRecrodPlugin 实例将会成为主数据源,可以省略 configName。最先创建的 ActiveRecrodPlugin 实例中的配置将默认成为主配置,此外还可以通过设置 configName为 DbKit.MAIN_CONFIG_NAME 常量来设置主配置。

非 web 环境下使用 ActiveRecord

ActiveRecordPlugin 可以独立于 java web 环境运行在任何普通的 java 程序中,使用方式极 度简单,相对于 web 项目只需要手动调用一下其 start() 方法即可立即使用。以下是代码示例:

public class ActiveRecordTest {
    public static void main(String[] args) {
    DruidPlugin dp = new DruidPlugin("localhost", "userName", "password"); ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);                 arp.addMapping("blog", Blog.class);

    // 与web环境唯一的不同是要手动调用一次相关插件的start()方法
    dp.start();
    arp.start();

    // 通过上面简单的几行代码,即可立即开始使用
    new Blog().set("title", "title").set("content", "cxt text").save(); Blog.me.findById(123);
}
}

注意:ActiveRecordPlugin 所依赖的其它插件也必须手动调用一下 start()方法,如上例中的 dp.start()。

JSRUN闪电教程系统是国内最先开创的教程维护系统, 所有工程师都可以参与共同维护的闪电教程,让知识的积累变得统一完整、自成体系。 大家可以一起参与进共编,让零散的知识点帮助更多的人。
X
支付宝
9.99
无法付款,请点击这里
金额: 0
备注:
转账时请填写正确的金额和备注信息,到账由人工处理,可能需要较长时间
如有疑问请联系QQ:565830900
正在生成二维码, 此过程可能需要15秒钟