Til*_*man 27 java join one-to-many jdbi dropwizard
我正在使用使用JDBI的dropwizard创建一个简单的REST应用程序.下一步是集成一个与另一个具有一对多关系的新资源.到目前为止,我无法弄清楚如何在我的DAO中创建一个方法来检索一个包含另一个表中对象列表的对象.
POJO表示将是这样的:
public class User {
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Account {
private int id;
private String name;
private List<User> users;
public Account(int id, String name, List<User> users) {
this.id = id;
this.name = name;
this.users = users;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<User> getUsers() {
return name;
}
public void setUsers(List<Users> users) {
this.users = users;
}
}
Run Code Online (Sandbox Code Playgroud)
DAO看起来应该是这样的
public interface AccountDAO {
@Mapper(AccountMapper.class)
@SqlQuery("SELECT Account.id, Account.name, User.name as u_name FROM Account LEFT JOIN User ON User.accountId = Account.id WHERE Account.id = :id")
public Account getAccountById(@Bind("id") int id);
}
Run Code Online (Sandbox Code Playgroud)
但是当方法将单个对象作为返回值(Account而不是List <Account>)时,似乎无法在Mapper类中访问resultSet的多行.我能找到的唯一解决方案可以在https://groups.google.com/d/msg/jdbi/4e4EP-gVwEQ/02CRStgYGtgJ中找到,但是这个解决方案也只返回一个具有单个对象的Set,优雅.(并且资源类不能正确使用.)
似乎有一种方法在流畅的API中使用Folder2.但我不知道如何正确地与dropwizard集成,我宁愿坚持使用dropwizard文档中建议的JDBI的SQL对象API.
是否真的没有办法在JDBI中使用SQL对象API获得一对多映射?这是数据库的基本用例,我认为我必须遗漏一些东西.
蒂尔曼,非常感谢所有的帮助
Til*_*man 30
好的,经过大量的搜索,我看到两种处理方式:
第一个选项是为每个列检索一个对象,并将其合并到资源的Java代码中(即在代码中进行连接而不是由数据库完成).这会产生类似的结果
@GET
@Path("/{accountId}")
public Response getAccount(@PathParam("accountId") Integer accountId) {
Account account = accountDao.getAccount(accountId);
account.setUsers(userDao.getUsersForAccount(accountId));
return Response.ok(account).build();
}
Run Code Online (Sandbox Code Playgroud)
这对于较小的连接操作是可行的,但对我来说似乎不是很优雅,因为这是数据库应该做的事情.但是,我决定采用这条路径,因为我的应用程序相当小,我不想写很多映射器代码.
在第二个选项是写一个映射,检索联接查询的结果,并将其映射到这样的对象:
public class AccountMapper implements ResultSetMapper<Account> {
private Account account;
// this mapping method will get called for every row in the result set
public Account map(int index, ResultSet rs, StatementContext ctx) throws SQLException {
// for the first row of the result set, we create the wrapper object
if (index == 0) {
account = new Account(rs.getInt("id"), rs.getString("name"), new LinkedList<User>());
}
// ...and with every line we add one of the joined users
User user = new User(rs.getInt("u_id"), rs.getString("u_name"));
if (user.getId() > 0) {
account.getUsers().add(user);
}
return account;
}
}
Run Code Online (Sandbox Code Playgroud)
然后DAO接口将有一个这样的方法:
public interface AccountDAO {
@Mapper(AccountMapper.class)
@SqlQuery("SELECT Account.id, Account.name, User.id as u_id, User.name as u_name FROM Account LEFT JOIN User ON User.accountId = Account.id WHERE Account.id = :id")
public List<Account> getAccountById(@Bind("id") int id);
}
Run Code Online (Sandbox Code Playgroud)
注意:如果使用非集合返回类型,您的抽象DAO类将静默编译,例如public Account getAccountById(...);
.但是,即使SQL查询找到多行,您的映射器也只会收到一行的结果集,您的映射器很乐意将其转换为单个用户的单个帐户.JDBI似乎强加LIMIT 1
了SELECT
具有非收集返回类型的查询.如果将它声明为抽象类,则可以在DAO中放置具体方法,因此一个选项是使用公共/受保护方法对来包装逻辑,如下所示:
public abstract class AccountDAO {
@Mapper(AccountMapper.class)
@SqlQuery("SELECT Account.id, Account.name, User.id as u_id, User.name as u_name FROM Account LEFT JOIN User ON User.accountId = Account.id WHERE Account.id = :id")
protected abstract List<Account> _getAccountById(@Bind("id") int id);
public Account getAccountById(int id) {
List<Account> accountList = _getAccountById(id);
if (accountList == null || accountList.size() < 1) {
// Log it or report error if needed
return null;
}
// The mapper will have given a reference to the same value for every entry in the list
return accountList.get(accountList.size() - 1);
}
}
Run Code Online (Sandbox Code Playgroud)
这对我来说似乎有点麻烦和低级,因为在处理关系数据时通常会有很多连接.我希望看到更好的方法或让JDBI使用SQL对象API支持抽象操作.
在JDBI v3中,可以使用@UseRowReducer来实现。在联接结果的每一行上都会调用行缩减器,您可以将其“累加”到单个对象中。您的情况下的简单实现如下所示:
public class AccountUserReducer implements LinkedHashMapRowReducer<Integer, Account> {
@Override
public void accumulate(final Map<Integer, Account> map, final RowView rowView) {
final Account account = map.computeIfAbsent(rowView.getColumn("a_id", Integer.class),
id -> rowView.getRow(Account.class));
if (rowView.getColumn("u_id", Integer.class) != null) {
account.addUser(rowView.getRow(User.class));
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以在返回联接的查询上应用此reducer:
@RegisterBeanMapper(value = Account.class, prefix = "a")
@RegisterBeanMapper(value = User.class, prefix = "u")
@SqlQuery("SELECT a.id a_id, a.name a_name, u.id u_id, u.name u_name FROM " +
"Account a LEFT JOIN User u ON u.accountId = a.id WHERE " +
"a.id = :id")
@UseRowReducer(AccountUserReducer.class)
Account getAccount(@Bind("id") int id);
Run Code Online (Sandbox Code Playgroud)
请注意,您User
和Account
行/ bean映射器可以保持不变;他们只知道如何分别映射用户表和帐户表的单个行。您的Account
类将需要一个addUser()
在每次调用行缩减器时都会调用的方法。
归档时间: |
|
查看次数: |
13578 次 |
最近记录: |