1

我们有一个巨大的数据库应用程序,必须对其进行重构(这有很多原因。最大的一个:安全性)。

我们已经拥有的:

  • 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
}

(这是一本有多个作者的书)。所有三个选项都有不同的优点和缺点:

  1. 我可以直接访问相应的Author模型,但是如果我通过 REST 请求Book模型,我可能现在不想要模型,但稍后。所以选项2会更好:
  2. Book我可以直接通过 REST请求模型。然后使用authorIds来获取相应的作者。但现在我不能简单地使用myBook.getAuthors().
  3. 这是 1. 和 2. 的混合体:如果我只请求Book仅包含 id 的 s Author,我可以执行以下操作: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.

4

1 回答 1

1

首先,您需要控制 JPA 层如何处理您的关系。我的意思是使用延迟加载与急切加载。这可以通过注释上的“fetch”选项轻松控制,如下所示:

@OneToMany(fetch=FetchType.Lazy)

这告诉 JPA,对于这个相关对象,只有在某些代码请求它时才加载它。在幕后,正在制作/创建一个动态“代理”对象。当您尝试访问此代理时,它足够聪明,可以出去执行另一个 SQL 来收集所需的位。在 Collection 的情况下,如果您迭代 Collection 中的项目,它甚至足够聪明地批量抓取底层对象。但是,请注意:对这些代理的访问必须在同一个常规会话中进行。底层 ORM 框架(不知道 Eclipselink 是如何工作的……我是 Hybernate 用户)不知道如何将子请求与正确的域对象相关联。当您使用 Flex BlazeDS 等运输框架时,这会产生更大的影响,

您可能还想设置您的级联策略,这可以通过“级联”选项来完成,例如

@OneToMany(cascade=CascadeType.ALL)

或者你可以给它一个类似的列表:

@OneToMany(cascade={CascadeType.MERGE, CascadeType.REMOVE})

一旦控制了从数据库中提取的内容,就需要查看如何编组域对象。您是否根据请求通过 JSON、XML 或混合方式发送此信息?您使用什么框架(Jackson、FlexJSON、XStream 等)?问题是,即使您将 fetch 类型设置为 Lazy,这些框架仍然会追踪相关对象,从而否定您所做的所有工作告诉它延迟加载。这是对编组/序列化方案更具体的地方:您需要弄清楚如何告诉您的框架什么要编组,什么不应该编组。同样,这将高度依赖于正在使用的任何框架。

于 2013-02-22T14:47:27.640 回答