简体   繁体   English

JPA用户和角色设计

[英]JPA Design of User and Roles

I have a User class and a Role class. 我有一个User类和一个Role类。 Both of these classes are JPA entities, and hence stored in a Person table and a Role table, as well as a corresponding link table Person_Role used for joins, since the association is many to many. 这两个类都是JPA实体,因此由于关联是多对多的,因此存储在Person表和Role表以及用于联接的对应链接Person_Role A user may have many roles, and a role may be assigned to many users. 一个用户可能具有许多角色,并且一个角色可能被分配给许多用户。

@Entity
@Table(name="role")
public class Role implements Comparable<Role>
{
   // data members
   @Id @GeneratedValue
   private int    id;              // primary key
   private String name;            // name of the role
   private String description;     // description of the role

   ...
} 

@Entity
@Table(name="person")
public class Person implements Comparable<Person>
{
   // data members
   @Id @GeneratedValue
   protected int       id;          // the primary key
   protected String    username;    // the user's unique user name
   protected String    firstName;   // the user's first name
   protected String    lastName;    // the user's last  name
   protected String    email;       // the user's work e-mail address
   @Transient
   protected String    history;     // chronological list of changes to the person
                                    // don't want to load this unless an explicit call to getHistory() is made
   @Transient
   protected Set<Role> roles;       // list of roles assigned to the user
                                    // don't want to load this unless an explicit call to getRoles() is made

   ...
}

The User entity is used extensively throughout the application, as it is a shared reference for many objects, and is used in many, many searches. User实体在整个应用程序中得到广泛使用,因为它是许多对象的共享引用,并且在很多搜索中使用。 99.99% of the time, the user's roles and history are not needed. 99.99%的时间不需要用户的角色和历史记录。 I'm new to JPA, and have been reading the "Java Persistence with Hibernate" book in order to learn. 我是JPA的新手,并且已经在阅读“ Hibernate的Java持久性”一书以进行学习。 As I understand lazy fetching, it will load all the corresponding User data from the database when any getXXX() method is called. 据我了解,延迟获取会在调用任何getXXX()方法时从数据库加载所有相应的User数据。

Ex: user.getFirstName() would cause a database hit and load all the data, including roles and history, for the user. 例如: user.getFirstName()将导致数据库命中并为用户加载所有数据,包括角色和历史记录。

I want to avoid this at all costs. 我不惜一切代价避免这种情况。 Its just needless in 99.99% of the use cases. 在99.99%的用例中,它是不必要的。 So, what's the best way to handle this? 那么,处理此问题的最佳方法是什么?

My initial thought is to mark the Set<Role> roles and Set<String> history in the User class as @Transient and manually query for the roles and history only when the user.getRoles() or user.getHistory() method is called. 我最初的想法是将User类中的Set<Role>角色和Set<String>历史记录标记为@Transient ,并仅在调用user.getRoles()user.getHistory()方法时手动查询角色和历史记录。

Thanks for any suggestions. 感谢您的任何建议。

It will not Load all the data just the the relative to 它不会仅加载相对于的所有数据

 Person entity = (Person) this.em.find(Person.class, id);

in lazy fetching it will issue a select statement from only the table person, as for protected Set<Role> roles; 在延迟获取中,它将仅从表人员发出选择语句,就像protected Set<Role> roles; it will not be loaded but replaced with a proxy object 它不会被加载,而是被代理对象替换

Hibernate uses a proxy object to implement lazy loading. Hibernate使用代理对象来实现延迟加载。 When we request to load the Object from the database, and the fetched Object has a reference to another concrete object, Hibernate returns a proxy instead of the concrete associated object. 当我们请求从数据库中加载对象,并且获取的对象具有对另一个具体对象的引用时,Hibernate返回一个代理而不是具体的关联对象。

Hibernate creates a proxy object using bytecode instrumentation (provided by javassist). Hibernate使用字节码检测(由Javassist提供)创建代理对象。 Hibernate creates a subclass of our entity class at runtime using the code generation library and replaces the actual object with the newly created proxy. Hibernate在运行时使用代码生成库创建我们的实体类的子类,并将实际对象替换为新创建的代理。

As I understand lazy fetching, it will load all the corresponding User data from the database when any getXXX() method is called. 据我了解,延迟获取会在调用任何getXXX()方法时从数据库加载所有相应的用户数据。

You can force JPA to be eager or lazy while fetching data from the database but first and foremost it depends on JPA provider. 您可以在从数据库中获取数据时强制JPA急于或懒惰,但首先要取决于JPA提供程序。 As described in JPA 2.1 specification, chapter 11.1.6: 如JPA 2.1规范第11.1.6章所述:

The FetchType enum defines strategies for fetching data from the database: FetchType枚举定义了从数据库中获取数据的策略:

 public enum FetchType { LAZY, EAGER }; 

The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. EAGER策略是对持久性提供程序运行时的要求 ,必须热切地获取数据。 The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. LAZY策略向持久性提供程序运行时提供了提示 ,即首次访问数据时应延迟获取数据。 The implementation is permitted to eagerly fetch data for which the LAZY strategy hint has been specified. 该实现被允许急切地获取已为其指定LAZY策略提示的数据。 In particular, lazy fetching might only be available for Basic mappings for which property-based access is used. 特别是,懒惰获取可能仅适用于使用基于属性的访问的Basic映射。

A nice presentation on how fetching strategies work and how performant they are in real-life scenarios you can find here . 您可以在这里找到有关获取策略如何工作以及它们在实际场景中的性能的很好的介绍。

Ex: user.getFirstName() would cause a database hit and load all the data, including roles and history, for the user. 例如:user.getFirstName()将导致数据库命中并为用户加载所有数据,包括角色和历史记录。

Data are retrieved either directly from the persistence context (usually it has a short lifespan) or indirectly from the underlying database (when it's not found in the transactional/shared caches). 可以直接从持久性上下文中检索数据(通常使用寿命很短),也可以从底层数据库中间接检索数据(在事务/共享缓存中找不到时)。 If entity manager is requested to get your entity object and it does not exist in the persistence context it needs to go deeper - into the database in the worst scenario. 如果要求实体管理器获取您的实体对象,并且在持久性上下文中不存在该实体对象,则需要更深入地了解-在最坏的情况下将其放入数据库中。

I want to avoid this at all costs. 我不惜一切代价避免这种情况。 Its just needless in 99.99% of the use cases. 在99.99%的用例中,它是不必要的。 So, what's the best way to handle this? 那么,处理此问题的最佳方法是什么?

An example approach: 一个示例方法:

@Entity
@NamedQuery(name="Person.getNameById", 
            query="SELECT p.name FROM Person p WHERE p.id = :id")
public class Person
{
   @Id @GeneratedValue
   protected int id;

   private String name; //the sole attribute to be requested

   @ManyToMany //fetch type is lazy by default
   @JoinTable
   protected Set<Role> roles; //not loaded until referenced or accessed
   ...
}

Usually the best way to go is the find method. 通常最好的方法是find方法。 It's perfect when you want to retrieve all non-relationship attributes at once: 当您想一次检索所有非关系属性时,它是完美的:

Person p = em.find(Person.class, id)

An alternative for you would be to use named query. 一种替代方法是使用命名查询。 It's useful when you need a single attribute or a small subset of attributes: 当您需要单个属性或一小部分属性时,这很有用:

String name = em.createNamedQuery("Person.getNameById", String.class)
                 .setParameter("id", id)
                 .getSingleResult() 

My initial thought is to mark the Set roles and Set history in the User class as @Transient and manually query for the roles and history only when the user.getRoles() or user.getHistory() method is called. 我最初的想法是将User类中的Set角色和Set历史记录标记为@Transient,并仅在调用user.getRoles()或user.getHistory()方法时手动查询角色和历史记录。

Transient attributes are not persisted in a database. 瞬态属性不会保留在数据库中。 Whatever you will set to these attributes it will stay in memory only. 无论您将这些属性设置为什么,它都只会保留在内存中。 I would prefer JPA doing it lazily. 我希望JPA懒惰地这样做。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM