ORM

配置

在使用数据库之前,需要给Jboot应用做一些配置,实际上,在任何需要用到数据库的应用中,都需要给应用程序做一些配置,让应用程序知道去哪里读取数据。

由于Jboot的数据库读取是依赖于JFinal,所以实际上只要是JFinal支持的数据库类型,Jboot都会支持,比如常用的数据库类型有:

  • Mysql
  • Oracle
  • SqlServer
  • postgresql
  • sqlite
  • 其他标准的数据库

在Jboot应用连接数据库之前,我们需要在resources目录下创建一个jboot.properties配置文件,并在jboot.properties编写内容如下:

  1. jboot.datasource.type=mysql
  2. jboot.datasource.url=jdbc:mysql://127.0.0.1:3306/jbootdemo
  3. jboot.datasource.user=root
  4. jboot.datasource.password=your_password

其中:

  • jboot.datasource.type 是配置数据库类型
  • jboot.datasource.url 是数据库请求URL地址
  • jboot.datasource.user 是数据库需要的账号
  • jboot.datasource.password 是数据库需要的密码

高级配置

除了 typeurluserpassword四个配置以外,jbootdatasource 还支持以下配置:

  • jboot.datasource.name 数据源的名称
  • jboot.datasource.driverClassName 驱动类名
  • jboot.datasource.connectionInitSql 连接初始化Sql
  • jboot.datasource.poolName 线程池名称
  • jboot.datasource.cachePrepStmt 缓存启用
  • jboot.datasource.prepStmtCacheSize 缓存大小
  • jboot.datasource.prepStmtCacheSqlLimit 缓存限制
  • jboot.datasource.maximumPoolSize 线程池大小
  • jboot.datasource.sqlTemplatePath sql文件路径
  • jboot.datasource.sqlTemplate sql文件,多个用英文逗号隔开
  • jboot.datasource.table 该数据源对应的表名,多个表用英文逗号隔开

更多的具体使用,特别是name、table等在分库分表章节会讲到。

Model

model是MVC设计模式中的M,但同时每个model也会对应一个数据库表,它充当 MVC 模式中的 Model 部分。以下是Model 定义示例代码:

  1. public class User extends JbootModel<User> {
  2. public static final User dao = new User().dao();
  3. }

以上代码中的 User 通过继承 Model,便立即拥有的众多方便的操作数据库的方法。在 User中声明的 dao 静态对象是为了方便查询操作而定义的,该对象并不是必须的。同时,model无需定义 getter、setter 方法,无需 XML 配置,极大降低了代码量。

以下是model常见的用法:

  1. // 创建name属性为James,age属性为25的User对象并添加到数据库
  2. new User().set("name", "James").set("age", 25).save();
  3. // 删除id值为25的User
  4. User.dao.deleteById(25);
  5. // 查询id值为25的User将其name属性改为James并更新到数据库
  6. User.dao.findById(25).set("name", "James").update();
  7. // 查询id值为25的user, 且仅仅取name与age两个字段的值
  8. User user = User.dao.findByIdLoadColumns(25, "name, age");
  9. // 获取user的name属性
  10. String userName = user.getStr("name");
  11. // 获取user的age属性
  12. Integer userAge = user.getInt("age");
  13. // 查询所有年龄大于18岁的user
  14. List<User> users = User.dao.find("select * from user where age>18");
  15. // 分页查询年龄大于18的user,当前页号为1,每页10个user
  16. Page<User> userPage = User.dao.paginate(1, 10, "select *", "from user
  17. where age > ?", 18);

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

@Table注解

@Table注解是给Model使用的,表示让Model映射到哪个数据库表,使用代码如下:

  1. @Table(tableName = "user", primaryKey = "id")
  2. public class User extends JbootModel <Company> {
  3. }

值得注意的是:

在Jboot应用中,我们几乎感受不到@Table这个注解的存在,因为这部分完全是代码生成器生成的,关于代码生成器,请查看 代码生成器章节。

Db + Record 模式

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

  1. // 创建name属性为James,age属性为25的record对象并添加到数据库
  2. Record user = new Record().set("name", "James").set("age", 25);
  3. Db.save("user", user);
  4. // 删除id值为25的user表中的记录
  5. Db.deleteById("user", 25);
  6. // 查询id值为25的Record将其name属性改为James并更新到数据库
  7. user = Db.findById("user", 25).set("name", "James");
  8. Db.update("user", user);
  9. // 获取user的name属性
  10. String userName = user.getStr("name");
  11. // 获取user的age属性
  12. Integer userAge = user.getInt("age");
  13. // 查询所有年龄大于18岁的user
  14. Page<Record> userPage = Db.paginate(1, 10, "select *", "from user where
  15. age > ?", 18);

或者,事务操作:

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

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

更多

请参考JFinal的文档:http://download.jfinal.com/download/3.2/jfinal-3.2-manual.pdf

多数据源

在Jboot中,使用多数据源非常简单。

在以上章节里,我们知道,要连接数据库需要做如下配置:

  1. jboot.datasource.type=mysql
  2. jboot.datasource.url=jdbc:mysql://127.0.0.1:3306/jbootdemo
  3. jboot.datasource.user=root
  4. jboot.datasource.password=your_password

假设我们再增加两个数据源,只需要在jboot.properties文件在添加如下配置即可:

  1. jboot.datasource.a1.type=mysql
  2. jboot.datasource.a1.turl=jdbc:mysql://127.0.0.1:3306/jboot1
  3. jboot.datasource.a1.tuser=root
  4. jboot.datasource.a1.tpassword=your_password
  5. jboot.datasource.a2.type=mysql
  6. jboot.datasource.a2.turl=jdbc:mysql://127.0.0.1:3306/jboot2
  7. jboot.datasource.a2.tuser=root
  8. jboot.datasource.a2.tpassword=your_password

这表示,我们又增加了数据源a1和数据源a2,在使用的时候,我们只需要做一下使用:

  1. Company company = new Company();
  2. company.setCid("1");
  3. company.setName("name");
  4. company.use("a1").save();

company.use("a1").save();表示使用数据源a1进行保存。

值得注意的是:

在多数据源应用中,很多时候,我们一个Model只有对应一个数据源,而不是一个Model对应多个数据源。假设Company只有在a1数据源中存在,在其他数据源并不存在,我们需要把a1数据源的配置修改如下:

  1. jboot.datasource.a1.type=mysql
  2. jboot.datasource.a1.url=jdbc:mysql://127.0.0.1:3306/jboot1
  3. jboot.datasource.a1.user=root
  4. jboot.datasource.a1.password=your_password
  5. jboot.datasource.a1.table=company
  6. jboot.datasource.a2.type=mysql
  7. jboot.datasource.a2.url=jdbc:mysql://127.0.0.1:3306/jboot2
  8. jboot.datasource.a2.user=root
  9. jboot.datasource.a2.password=your_password
  10. jboot.datasource.a1.table=user,xxx(其他非company表)

这样,company在a1数据源中存在,Jboot在初始化的时候,并不会去检查company在其他数据源中是否存在,同时,代码操作company的时候,不再需要use,代码如下:

  1. Company company = new Company();
  2. company.setCid("1");
  3. company.setName("name");
  4. //company.use("a1").save();
  5. company.save();

代码中不再需要 use("a1") 指定数据源,因为company只有一个数据源。

分库和分表

在Jboot中,分表是通过sharding-jdbc( 网址:https://github.com/shardingjdbc/sharding-jdbc) 来实现的,所以,在了解Jboot的分表之前,请先阅读了解sharding-jdbc的配置信息。

分库分表相关demo: 点击这里

分库

分库意味你有多个数据库,每个数据库会对应一个数据源。

例如,我们的应用有三个数据库,分别是 db1,db2,db3,那么需要我们在 jboot.properties 配置文件里配置上三个数据,配置如下:

  1. jboot.datasource.db1.url = jdbc:mysql://127.0.0.1:3306/db1
  2. jboot.datasource.db1.user = root
  3. jboot.datasource.db1.password =
  4. jboot.datasource.db2.url = jdbc:mysql://127.0.0.1:3306/db2
  5. jboot.datasource.db2.user = root
  6. jboot.datasource.db2.password =
  7. jboot.datasource.db3.url = jdbc:mysql://127.0.0.1:3306/db3
  8. jboot.datasource.db3.user = root
  9. jboot.datasource.db3.password =

我们希望在分库的时候,通过Model的主键ID进行hashcode进行取模,决定分库。因此需要编写分库策略,代码如下:

  1. public class UserDatabaseShardingStrategyConfig implements ShardingStrategyConfiguration {
  2. @Override
  3. public ShardingStrategy build() {
  4. return shardingStrategy;
  5. }
  6. private ShardingStrategy shardingStrategy = new ShardingStrategy() {
  7. @Override
  8. public Collection<String> getShardingColumns() {
  9. //根据id进行分库
  10. return Sets.newHashSet("id");
  11. }
  12. @Override
  13. public Collection<String> doSharding(Collection<String> availableTargetNames, Collection<ShardingValue> shardingValues) {
  14. ListShardingValue shardingValue = (ListShardingValue) shardingValues.stream().findFirst().get();
  15. String dbName = "db" + Math.abs(shardingValue.getValues().iterator().next().toString().hashCode()) % 3;
  16. System.out.println("插入数据到库:" + dbName);
  17. //返回通过计算得到的表
  18. return Sets.newHashSet(dbName);
  19. }
  20. };
  21. }

编写好分库策略后,需要给Model配置上分库策略:

  1. @Table(tableName = "tb_user",
  2. primaryKey = "id",
  3. // 具体的表tb_user${0..2} 表示有三张表 tb_user0,tb_user1,tb_user2,
  4. // main 是默认数据源的名称
  5. actualDataNodes = "main.tb_user${0..2}",
  6. //分表策略
  7. databaseShardingStrategyConfig = UserDatabaseShardingStrategyConfig.class
  8. )
  9. public class UserModel extends JbootModel<UserModel> {
  10. //geter setter
  11. }

分表

demos

例如:有一个userModel,我们希望能进行分为三张表,通过id的hashcode进行取模,代码如下:

  1. @Table(tableName = "tb_user",
  2. primaryKey = "id",
  3. // 具体的表tb_user${0..2} 表示有三张表 tb_user0,tb_user1,tb_user2,
  4. // main 是默认数据源的名称
  5. actualDataNodes = "main.tb_user${0..2}",
  6. //分表策略
  7. tableShardingStrategyConfig = UserTableShardingStrategyConfig.class
  8. )
  9. public class UserModel extends JbootModel<UserModel> {
  10. //geter setter
  11. }

编写UserModel的分表策略 UserTableShardingStrategyConfig,代码如下:

  1. public class UserTableShardingStrategyConfig implements ShardingStrategyConfiguration {
  2. @Override
  3. public ShardingStrategy build() {
  4. return shardingStrategy;
  5. }
  6. private ShardingStrategy shardingStrategy = new ShardingStrategy() {
  7. @Override
  8. public Collection<String> getShardingColumns() {
  9. //根据id进行分表
  10. return Sets.newHashSet("id");
  11. }
  12. @Override
  13. public Collection<String> doSharding(Collection<String> availableTargetNames, Collection<ShardingValue> shardingValues) {
  14. ListShardingValue shardingValue = (ListShardingValue) shardingValues.stream().findFirst().get();
  15. String tableName = "tb_user" + Math.abs(shardingValue.getValues().iterator().next().toString().hashCode()) % 3;
  16. System.out.println("插入数据到表:" + tableName);
  17. //返回通过计算得到的表
  18. return Sets.newHashSet(tableName);
  19. }
  20. };
  21. }

编写配置文件:

  1. jboot.datasource.type=mysql
  2. jboot.datasource.url=jdbc:mysql://127.0.0.1:3306/jbootsharding
  3. jboot.datasource.user=root
  4. jboot.datasource.password=
  5. jboot.datasource.shardingEnable=true

进行UserModel保存到数据库

  1. @RequestMapping("/sharding")
  2. public class ShardingController extends JbootController {
  3. public void index() {
  4. UserModel user = new UserModel();
  5. user.setId(StringUtils.uuid());
  6. user.setName("Michael yang");
  7. user.save();
  8. renderText("插入数据成功,请查看数据库...");
  9. }
  10. public static void main(String[] args) {
  11. Jboot.run(args);
  12. }
  13. }

具体demo请参考:

https://gitee.com/fuhai/jboot/tree/master/src/test/java/sharding