我们有一个巨大的数据库应用程序,必须对其进行重构(这有很多原因。最大的一个:安全性)。
我们已经拥有的:
- MySQL 数据库
- 超过 100 个表的 JPA2 (Eclipselink) 类
- 直接访问数据库的客户端应用程序
需要有什么:
- REST接口
- 通过数据库使用角色登录/注销
到目前为止我所做的:
- 使用 Spring Security 3.1.1 设置 Spring MVC 3.2.1
- 使用自定义
UserDetailsService
(仅包含用于测试 atm 的静态数据) - 创建了一些用于测试的控制器(简单地接收/提供数据)
设计问题:
- 我们的数据库中有 maaaaany @OneToMany 和 @ManyToMany 关系
1.:(重要)
如果我发送带有所有子对象的整个对象树作为响应,我可能会一次发送整个数据库。
所以我需要一种方法来请求例如“所有文章”。但它应该省略所有子对象。我昨天试过了,我收到的对象有很多兆字节:
@PersistenceContext
private EntityManager em;
@RequestMapping(method=RequestMethod.GET)
public @ResponseBody List<Article> index() {
List<Article> a = em.createQuery("SELECT a FROM Article a", Article.class).getResultList();
return a;
}
2.:(重要)
如果客户收到一篇文章,此时我们可以简单地调用article.getAuthor()
,JPA 会做一个SELECT a FROM Author a JOIN Article ar WHERE ar.author_id = ?
.
使用 REST,我们可以向/authors/{id}
. 但是:这样我们就不能在客户端使用我们旧的 JPA 模型,因为模型包含Author author
而不是Long author_id
.
我们必须重写每个模型还是有更简单的方法?
3.:(不太重要)
身份验证:是否使其无状态?到目前为止,我从未使用过无状态身份验证,但 Spring 似乎对它有某种支持。当我查看网络上的一些示例实现时,我有安全方面的顾虑:每个请求都会发送用户名和密码。这不可能是正确的方法。
如果有人知道一个很好的解决方案,请告诉我。否则我只会使用标准的 HTTP 会话。
4.:
设计客户端模型的最佳方法是什么?
public class Book {
int id;
List<Author> authors; //option1
List<Integer> authorIds; //option2
Map<Integer, Author> idAuthorMap; //option3
}
(这是一本有多个作者的书)。所有三个选项都有不同的优点和缺点:
- 我可以直接访问相应的
Author
模型,但是如果我通过 REST 请求Book
模型,我可能现在不想要模型,但稍后。所以选项2会更好: Book
我可以直接通过 REST请求模型。然后使用authorIds
来获取相应的作者。但现在我不能简单地使用myBook.getAuthors()
.- 这是 1. 和 2. 的混合体:如果我只请求
Book
仅包含 id 的 sAuthor
,我可以执行以下操作:idAuthorMap.put(authorId, null)
.
但也许有一个 Java 库可以为我处理所有的东西?!
现在就是这样。感谢你们 :)
可能的解决方案:
问题:只选择我需要的数据。这意味着或多或少地忽略每个@ManyToMany
, @OneToMany
,@ManyToOne
关系。
解决方案:使用@JsonIgnore
和/或@JsonIgnoreProperties
.
问题:每个被忽略的关系都应该在不修改数据模型的情况下轻松获取。
解决方案:示例模型:
class Book {
int bId;
Author author; // has @ManyToOne
}
class Author {
int aId;
List<Book> books; // has @OneToMany
}
现在我可以通过 REST: 获取一本书GET /books/4
,结果将如下所示(因为我通过 忽略所有关系@JsonIgnore
):{"bId":4}
然后我必须创建另一个路由来接收相关作者:GET /books/4/author
。将返回:{"aId":6}
。
向后:GET /authors/6/books
-> [{"bId":4},{"bId":42}]
。
每个@ManyToMany
, @OneToMany
,都会有一条路线@ManyToOne
,但仅此而已。所以这将不存在:GET /authors/6/books/42
. 客户应该使用GET /books/42
.