3.2.6.2. DataManager
DataManager
接口在中间层和客户端层提供 CRUD 功能,是一种通用工具,用于从数据库加载实体关系图并保存已更改的游离实体实例。
参考 在 CUBA 中进行数据处理 指南,了解在 CUBA 中如何使用 DataManager API 处理不同的数据访问。 |
有关 DataManager 与 EntityManager 之间差异的信息,请参阅 DataManager 与 EntityManager。 |
实际上,DataManager
只是委托给一个数据存储实现,并在需要时处理跨数据库引用。当使用标准 RdbmsStore
处理存储在关系型数据库中的实体时,下面描述的大多数实现细节都有效。对于另一种类型的数据存储,除接口方法名称之外的所有内容都可能不同。为简单起见,DataManager 在没有另外说明时指的是 基于 RdbmsStore 的 DataManager。
下面列出了 DataManager
的方法:
-
load(Class)
- 加载指定类的实体。此方法是流式 API 的入口点:@Inject private DataManager dataManager; private Book loadBookById(UUID bookId) { return dataManager.load(Book.class).id(bookId).view("book.edit").one(); } private List<BookPublication> loadBookPublications(UUID bookId) { return dataManager.load(BookPublication.class) .query("select p from library_BookPublication p where p.book.id = :bookId") .parameter("bookId", bookId) .view("bookPublication.full") .list(); }
-
loadValues(String query)
- 通过查询纯数值加载键值对。此方法是流式 API 的入口点:List<KeyValueEntity> list = dataManager.loadValues( "select o.customer, sum(o.amount) from demo_Order o " + "where o.date >= :date group by o.customer") .store("legacy_db") (1) .properties("customer", "sum") (2) .parameter("date", orderDate) .list();
1 - 指定实体所在的数据存储。 如果实体位于主数据存储,那么可以忽略这个方法。 2 - 指定返回的 KeyValueEntity
实体中的属性名称。 属性的顺序必须与查询结果集的列对应。 -
loadValue(String query, Class valueType)
- 通过查询纯数值加载单个值。此方法是流式 API 的入口点:BigDecimal sum = dataManager.loadValue( "select sum(o.amount) from demo_Order o " + "where o.date >= :date group by o.customer", BigDecimal.class) .store("legacy_db") (1) .parameter("date", orderDate) .one();
1 - 指定实体所在的数据存储。 如果实体位于主数据存储,那么可以忽略这个方法。 -
load(LoadContext)
,loadList(LoadContext)
– 根据传递给它的LoadContext
对象的参数加载实体。LoadContext
必须包含 JPQL 查询语句或实体标识符。如果两者都定义的话,则使用查询语句而忽略实体标识符。例如:
@Inject private DataManager dataManager; private Book loadBookById(UUID bookId) { LoadContext<Book> loadContext = LoadContext.create(Book.class) .setId(bookId).setView("book.edit"); return dataManager.load(loadContext); } private List<BookPublication> loadBookPublications(UUID bookId) { LoadContext<BookPublication> loadContext = LoadContext.create(BookPublication.class) .setQuery(LoadContext.createQuery("select p from library_BookPublication p where p.book.id = :bookId") .setParameter("bookId", bookId)) .setView("bookPublication.full"); return dataManager.loadList(loadContext); }
-
loadValues(ValueLoadContext)
- 加载键值对列表。该方法接受ValueLoadContext
,定义纯数值的查询语句和键值列表。返回包含KeyValueEntity
实例的列表。例如:ValueLoadContext context = ValueLoadContext.create() .setQuery(ValueLoadContext.createQuery( "select o.customer, sum(o.amount) from demo_Order o " + "where o.date >= :date group by o.customer") .setParameter("date", orderDate)) .addProperty("customer") .addProperty("sum"); List<KeyValueEntity> list = dataManager.loadValues(context);
-
getCount(LoadContext)
- 返回传递给方法的查询语句的记录数。可能的情况下,RdbmsStore
中的标准实现使用与原始查询相同的条件执行select count()
查询,以获得最佳性能。 -
commit(CommitContext)
– 将CommitContext
中传递的一组实体保存到数据库中。必须分别指定用于更新和删除的实体的集合。该方法返回 EntityManager.merge() 返回的实体实例集合,实际上这些就是刚刚在 DB 中更新的新实例。后续的操作需要使用这些返回的实例,以防止数据丢失或造成乐观锁。通过使用
CommitContext.getViews()
获得的视图映射为每个保存的实例设置视图,这样可以确保返回实体中包含需要的属性。DataManager
可以为保存的实体进行 bean 验证。保存实体集合的示例:
@Inject private DataManager dataManager; private void saveBookInstances(List<BookInstance> toSave, List<BookInstance> toDelete) { CommitContext commitContext = new CommitContext(toSave, toDelete); dataManager.commit(commitContext); } private Set<Entity> saveAndReturnBookInstances(List<BookInstance> toSave, View view) { CommitContext commitContext = new CommitContext(); for (BookInstance bookInstance : toSave) { commitContext.addInstanceToCommit(bookInstance, view); } return dataManager.commit(commitContext); }
-
reload(Entity, View)
- 使用视图从数据库重新加载指定实例的便捷方法。委托给load()
方法执行。 -
remove(Entity)
- 从数据库中删除指定的实例。委托给commit()
方法执行。 -
create(Class)
- 在内存中创建给定实体的实例。这是一个便捷的方法,委托给了Metadata.create()
。 -
getReference(Class, Object)
- 返回一个实体实例,该实例可以用作对数据库中存在的对象的引用。例如,如果要创建
User
,则必须设置用户所属的Group
。如果知道 group ID,可以从数据库加载然后设置给用户。此方法可以避免不必要的数据库多次访问:user.setGroup(dataManager.getReference(Group.class, groupId)); dataManager.commit(user);
引用也可用于通过 id 删除现有对象:
dataManager.remove(dataManager.getReference(Customer.class, customerId));
- 查询
-
当系统使用关系型数据库时,用JPQL查询语句加载数据。参阅 JPQL 函数、不区分大小写的子串搜索 和 JPQL 中的宏 章节了解 CUBA 中的 JPQL 和 JPA 标准之间的差异。另外需要注意,
DataManager
只能执行 "select" 查询。流式接口的
query()
方法可以接收完整的或者省略的查询语句。如果使用省略的查询语句,需要符合下面的规则:-
可以省略
"select <alias>"
子句。 -
如果
"from"
子句包含单一实体,而且又不想用实体别名,则可以省略"from <entity> <alias> where"
子句。此时,框架会使用e
作为该实体的别名。 -
可以使用位置标记查询条件并同时将调价值作为额外的参数传递给
query()
方法。
示例:
// named parameter dataManager.load(Customer.class) .query("e.name like :name") .parameter("name", value) .list(); // positional parameter dataManager.load(Customer.class) .query("e.name like ?1", value) .list(); // case-insensitive positional parameter dataManager.load(Customer.class) .query("e.name like ?1 or e.email like ?1", "(?i)%joe%") .list(); // multiple positional parameters dataManager.load(Order.class) .query("e.date between ?1 and ?2", date1, date2) .list(); // omitting "select" and using a positional parameter dataManager.load(Order.class) .query("from sales_Order o, sales_OrderLine l " + "where l.order = o and l.product.name = ?1", productName) .list();
需要注意的是,位置标记条件参数只在流式接口支持。
LoadContext.Query
还是只支持命名条件参数。 -
- 事务
-
DataManager 总是启动一个新的事务并在操作完成时提交事务,从而返回游离状态的实体。在中间层,如果需要实现复杂的事务行为,可以使用 TransactionalDataManager。
- 部分实体
-
部分(Partial) 实体是一个实体实例,这个实例的属性可以是已加载的本地属性的一个子集。默认情况下,DataManager 根据视图加载部分实体(事实上,
RdbmsStore
只是将视图的 loadPartialEntities 属性设置为 true 并将其传递给 EntityManager )。在下面这些情况下,DataManager 会加载所有本地属性,视图仅用来获取引用: