Lealone ORM 是一个简洁优雅的类型安全的 ORM 框架,支持链式(或流式) API,能杜绝 SQL 注入。
在建表时,Lealone ORM 可以为每个表自动生成对应的模型类代码,这些代码不需要应用开发人员修改维护。
应用开发人员负责编写的代码无需导入 Lealone ORM 的任何东西(比如类、接口、注解)。
下文通过一个例子来介绍 Lealone ORM 大多数常用的功能,比如单表查添删改操作、多表关联查询、事务操作等等。
该例子创建了 customer 和 order 两张表,它们两者存在关联关系。
文章最后有完整的 Java 代码。
1. 建表
set @packageName 'org.lealone.examples.orm.generated'; -- 生成的模型类所在的包名set @srcPath './src/main/java'; -- 生成的模型类对应的源文件所在的根目录-- 创建customer表,会生成一个名为Customer的模型类create table if not exists customer (id long primary key,name char(10),notes varchar,phone int) package @packageName generate code @srcPath;-- 创建order表,会生成一个名为Order的模型类-- order是关键字,所以要用特殊方式表示create table if not exists `order` (customer_id long,order_id int primary key,order_date date,total double,FOREIGN KEY(customer_id) REFERENCES customer(id)) package @packageName generate code @srcPath;
这一步用于创建 customer 和 order 表,加了新的扩展语法, 如果指定 generate code,会生成 Customer 和 Order 两个模型类:
public class Customer extends Model<Customer> {public static final Customer dao = new Customer(null, ROOT_DAO);public final PLong<Customer> id;public final PString<Customer> name;public final PString<Customer> notes;public final PInteger<Customer> phone;
生成的 Customer 模型类的代码是不用修改的,采用的是一种简化的充血模型。
可以基于模型类的字段来构建出跟普通 SQL 极其相似的类型安全的 DSL。
因为 customer 和 order 两张表存在关联关系,所以生成的代码也体现了这种关系:
public Customer addOrder(Order m) {m.setCustomer(this);super.addModel(m);return this;}public Customer addOrder(Order... mArray) {for (Order m : mArray)addOrder(m);return this;}public List<Order> getOrderList() {return super.getModelList(Order.class);}
应用开发人员不需要去了解 Customer 模型类的代码实现细节, 只需要通过 IDE 的智能提示功能看看它提供了哪些可用字段和方法即可。
2. 单表 crud 操作
2.1 insert
Customer c = new Customer();c.id.set(1001);c.name.set("rob");c.phone.set(12345678);c.insert();
或者使用更简洁的链式风格:
new Customer().id.set(1001).name.set("rob").phone.set(12345678).insert();
自动生成的 Customer 类不是 POJO,所以并没有使用常规的 setter/getter 方法, 而是用 c.name.set(“rob”) 替换 c.setName(“rob”),这样的设计是有特别考量的。
Customer 类继承自 org.lealone.orm.Model,因为继承有先天的缺陷, 比如将来在 Model 这个父类中新增一些方法就有可能跟它的子类出现方法名冲突, 所以 Customer 类这样的 Model 子类使用 final 字段比使用 setter/getter 更安全,Model 类将来更容易扩展和维护。
除了继承原因外,使用 final 字段更灵活,比如可以使用 c.name.startsWith(“rob”) 作为查询条件,可以扩展出无数的方法。
2.2 find
// find oneCustomer c = Customer.dao.where().id.eq(1001).findOne();// find listList<Customer> list = Customer.dao.id.eq(1001).findList();
在生成的模型类代码里面会有一个 static final 的名为 dao 的字段,通过 dao 来构建链式风格的查询语句。
where() 是可选的,加上去能让代码更易读,一看跟在它后面的调用代码就知道都是用来构建查询条件的。
2.3 update
// 单记录 updatec.notes.set("test").update();// 批量 updateCustomer.dao.notes.set("batch update").where().name.startsWith("rob").update();
单记录 update 是针对通过 find 方法返回的实例的,批量 update 需要通过 dao 来构建 update 语句。
2.4 delete
// 单记录 deletec.delete();// 批量 deleteCustomer.dao.where().name.startsWith("rob").delete();
跟 update 一样,单记录 delete 也是针对通过 find 方法返回的实例的,批量 delete 需要通过 dao 来构建 delete 语句。
2.5 分页查询
// countint count = dao.findCount();// offset、limitList<Customer> list = dao.offset(0).limit(1).findList();
注意,offset 是从0开始的。
2.6 JSON
Customer c = new Customer();c.name.set("rob");c.phone.set(12345678);String json = c.encode(); //编码为 JSON 字符串c = Customer.decode(json); //从 JSON 字符串解码出 Customer 对象
3. 关联查询
3.1 批量增加有关联的记录
Order o1 = new Order().orderId.set(2001).orderDate.set("2018-01-01");Order o2 = new Order().orderId.set(2002).orderDate.set("2018-01-01");Customer customer = new Customer().id.set(1002).name.set("customer1");customer.addOrder(o1, o2).insert();// 调用addOrder后,Order的customerId字段会自动对应Customer的id字段assertEquals(o1.customerId.get(), customer.id.get());
3.2 两表join
Customer c = Customer.dao;Order o = Order.dao;customer = c.join(o).on().id.eq(o.customerId).where().id.eq(1002).findOne();// 一个customer对应两个OrderList<Order> orderList = customer.getOrderList();assertEquals(2, orderList.size());assertTrue(customer == orderList.get(0).getCustomer());
Lealone ORM 框架实现关联查询时不会像其他 ORM 框架那样产生 N + 1 问题, 会根据查询语句一次性把满足条件的记录查找出来,然后再创建所有的模型类的实例。
4. 事务
try {Customer.dao.beginTransaction(); // 开始一个新事务new Customer().id.set(1003).name.set("rob3").insert();new Customer().id.set(1004).name.set("rob4").insert();Customer.dao.commitTransaction(); // 提交事务} catch (Exception e) {Customer.dao.rollbackTransaction(); // 回滚事务e.printStackTrace();}
事务默认采用的是自动提交模式,调用 beginTransaction() 后事务变成手动提交模式。
5. 完整例子
下载项目 lealone-orm-demo
源码编译与打包请执行 build -p
运行执行 build -r
6. 可能出现的问题
如果执行 build -p 找不到 Lealone 的依赖包, 需要下载以下项目的代码:
执行 build -i 把它们安装到本地的maven仓库即可。
7. 更多例子
8. 完整的代码例子
package org.lealone.examples.orm;import static org.junit.Assert.assertEquals;import static org.junit.Assert.assertTrue;import java.sql.Connection;import java.sql.DriverManager;import java.sql.Statement;import java.util.List;import org.lealone.examples.orm.generated.Customer;import org.lealone.examples.orm.generated.Order;public class OrmDemo {public static void main(String[] args) throws Exception {createTable();testCrud();testJoin();testTransaction();}// ------------ 以下代码中出现的的 where()都是可选的 ------------// 单表crud操作static void testCrud() {// insertnew Customer().id.set(1001).name.set("rob").phone.set(12345678).insert();Customer dao = Customer.dao;// find oneCustomer c = dao.where().id.eq(1001).findOne();assertEquals("rob", c.name.get());// find listList<Customer> list = dao.id.eq(1001).findList();assertEquals(1, list.size());// countint count = dao.findCount();assertEquals(1, count);// 单记录 updatec.notes.set("test").update();c = dao.where().id.eq(1001).findOne();assertEquals("test", c.notes.get());// 批量 updatedao.notes.set("batch update").where().name.startsWith("rob").update();c = dao.id.eq(1001).findOne();assertEquals("batch update", c.notes.get());// 分页查询list = dao.offset(0).limit(1).findList();assertEquals(1, list.size());// deletedao.where().id.eq(1001).delete();count = dao.findCount();assertEquals(0, count);}// 两表关联查询static void testJoin() {// 批量增加有关联的记录Order o1 = new Order().orderId.set(2001).orderDate.set("2018-01-01");Order o2 = new Order().orderId.set(2002).orderDate.set("2018-01-01");Customer customer = new Customer().id.set(1002).name.set("customer1");customer.addOrder(o1, o2).insert();// 调用addOrder后,Order的customerId字段会自动对应Customer的id字段assertEquals(o1.customerId.get(), customer.id.get());// 关联查询Customer c = Customer.dao;Order o = Order.dao;customer = c.join(o).on().id.eq(o.customerId).where().id.eq(1002).findOne();// 一个customer对应两个OrderList<Order> orderList = customer.getOrderList();assertEquals(2, orderList.size());assertTrue(customer == orderList.get(0).getCustomer());}// 测试事务static void testTransaction() {try {Customer.dao.beginTransaction(); // 开始一个新事务new Customer().id.set(1003).name.set("rob3").insert();new Customer().id.set(1004).name.set("rob4").insert();Customer.dao.commitTransaction(); // 提交事务} catch (Exception e) {Customer.dao.rollbackTransaction(); // 回滚事务e.printStackTrace();}int count = Customer.dao.where().id.eq(1003).or().id.eq(1004).findCount();assertEquals(2, count);}// 执行建表脚本,同时自动生成对应的模型类的代码static void createTable() throws Exception {String jdbcUrl = "jdbc:lealone:embed:test";System.setProperty("lealone.jdbc.url", jdbcUrl);try (Connection conn = DriverManager.getConnection(jdbcUrl); Statement stmt = conn.createStatement()) {stmt.executeUpdate("RUNSCRIPT FROM './src/main/resources/tables.sql'");}}}
