如何将不同记录的数据添加到单个记录中?

如果没有时间,请看一下这个例子

我有两种类型的用户,临时用户和永久用户。

临时用户使用系统作为访客只需提供他们的名字并使用它,但系统需要跟踪它们。

永久用户是那些已注册且永久的用户。

一旦用户为自己创建永久记录,我需要将用户作为访客时所跟踪的所有信息复制到他的永久记录中。

课程如下,

@Entity public class PermUser{ @Id @GeneratedValue private long id; @OneToMany private List Favorites favorites; .... } @Entity public class Favorites { @Id @GeneratedValue private long id; @OneToMany (cascade = CascadeType.ALL) @LazyCollection(LazyCollectionOption.FALSE) private List  items; ... } @Entity public class FavoriteItems { @Id @GeneratedValue private long id; private int quantity; @ManyToOne private Ball ball; .. } @Entity public class TempUser extends PermUser{ private String date; .... } 

问题是:

如果我克隆了tempUser对象,我也会复制id参数,所以当保存perm用户对象时,它会显示一条消息,如“密钥输入’10’为密钥…”,我无法先删除tempUser然后保存permUser好像保存permUser失败我会错过数据。 如果我尝试在没有项目ID的情况下单独复制每个最喜欢的项目球,那么这将不是一种有效的方法。

示例( 一句话中的问题:如图所示,用户可能有多个TempUser记录和一个PermUser记录,因此我需要将所有TempUser记录的信息添加到该单个PermUser记录中。)

  Type of record | name | favorites | date | | | 1)TempUser | Jack | 2 items | 1/1/2013 2)TempUser | Jack | 3 items | 1/4/2013 --------------------------------------------------------------------------- PermUser | Jack | 5 items ( 2 + 3 items from his temp records) 

*请注意,我需要找到一个解决方案,如果尝试新的解决方案而不是克隆对象,则无需关心。

我有两个不同类的原因是tempUser有很少的附加属性,我可能还需要将几个tempUsers的collections夹添加到一个permUsercollections夹列表中。 并且如上所述,用户可以具有许多不同的不相关的临时记录

如果我错过了什么,请原谅我,但我不认为TempUserPermUser应该是不同的类。 TempUser扩展了PermUser ,这是一种“is-a”关系。 显然,临时用户不是永久用户。 你的问题没有给出足够的信息来certificate它们与众不同 – 也许它们是同一个类,差异可以表示为一些新的属性? 例如:

 @Entity public class User{ @OneToMany(cascade = CascadeType.ALL) private List Favorites favorites; private boolean isTemporary; .... } 

从临时到永久的“转换”可以由某个控制器处理,确保isTemporary = false并且永久用户的其他属性被适当地设置。 这将完全支持克隆问题,并且在您的数据库上会更容易。

我刚遇到同样的问题。 我一直在挖掘像SO这样的董事会中的许多有趣的文章和问题,直到我有足够的灵感。

起初我还想为不同类型的用户提供子类。 事实certificate,这个想法本身就是一个设计缺陷:

不要使用inheritance来定义角色!

更多信息在这里微妙的设计:inheritance与角色

将用户视为一个大容器,它只包含其他实体,如凭据,首选项,联系人,项目,用户信息等。

考虑到这一点,您可以轻松更改某些用户的某些能力/行为,

当然,您可以定义许多用户可以玩的角色。 扮演相同角色的用户将具有相同的function。

如果您有许多实体/对象相互依赖,那么您应该想到一种构建机制/模式,它以明确定义的方式设置某个用户角色。

一些想法: JPA实体实例化的正确方法

如果您有一个用户的建造者/工厂,那么您的另一个问题就不再那么复杂了。

示例(非常基本,不要期望太多!)

 public void changeUserRoleToPermanent (User currentUser) { UserBuilder builder = new UserBuilder(); builder.setRole(Role.PERMANENT); // builder internally does all the plumping // copy the stuff you want to keep builder.setId(user.getId); builder.setPrefences(); // ... User newRoleUser = builder.build(); newRoleUser = entityManager.merge(newRoleUser); entitymanager.detach(currentUser); // delete old stuff entityManager.remove(currentUser.getAccountInfo()); // Changed to different implementaion... } 

我承认,这是一些工作,但一旦你准备好基础设施,你将有很多可能性! 然后你可以快速“发明”新东西!

我希望我能传播一些想法。 对不起我的悲惨英语我很抱歉。

因为我同意先前的评论,如果有可能你应该重新评估这些实体,但如果这是不可能的,我建议你从数据库中返回一般用户,然后将该用户作为PermUser或TempUser,这两者都是用户,基于某些标准的存在。

对于问题的第2部分:

您正在使用CascadeType.ALL作为favorites关系。 这包括CascadeType.REMOVE ,这意味着对用户的删除操作将级联到该实体。 因此,请指定不包含CascadeType.REMOVECascadeType值数组。 见http://webarch.kuzeko.com/2011/11/hibernate-understanding-cascade-types/ 。

我要建议的可能不是OO,但希望会有效。 我很高兴保持PermUserTempUser分开不扩展它,也不将它们绑定到is-a关系中。 因此,我将在数据库中为TempUser设置两个单独的表,为PermUser设置一个表,从而将它们视为两个单独的实体。 许多人会发现它是多余的……但请继续阅读……我们都知道……有时冗余是好的..所以现在……

1)我不知道TempUser何时想成为PermUser 。 所以我将永远将所有TempUsers放在单独的表中。

2)如果用户总是想成为TempUser,我该怎么办? 我还有单独的TempUser表来引用..

3)我假设当TempUser想要成为PermUser时,您正在读取他的TempUser名称以获取他作为TempUser的记录。

所以现在你的工作很简单。 所以现在当TempUser想成为PermUser时,你要做的就是复制TempUser对象,填充你需要的属性并用它创建一个新的PermUser对象。 之后,如果您想要或删除它,您可以保留您的TempUser记录.. 🙂

此外,如果您保留TempUsers ,您TempUsers知道有多少TempUsers实际上是永久性的 ,并且还知道TempUser成为永久性的平均时间。

我认为你应该做一个手动深度克隆。 不完全是克隆,因为您必须将来自多个tempUser的数据合并到单个permUser。 您可以使用reflection和可选注释来自动复制信息。

要自动将现有对象中的字段复制到新对象,可以按照此示例进行操作。 它不是一个深刻的克隆,但可以帮助你作为起点。

“c”类用作参考。 src和dest必须是’c’的实例或’c’的子子句的实例。 该方法将复制’c’中定义的属性和’c’的超类。

 public static  E copyObject(E dest, E src, Class c) throws IllegalArgumentException, IllegalAccessException{ // TODO: You may want to create new instance of 'dest' here instead of receiving one as parameter if (!c.isAssignableFrom(src.getClass())) { throw new IllegalArgumentException("Incompatible classes: " + src.getClass() + " - " + c); } if (!c.isAssignableFrom(dest.getClass())) { throw new IllegalArgumentException("Incompatible classes: " + src.getClass() + " - " + c); } while (c != null && c != Object.class) { for (Field aField: c.getDeclaredFields()) { // We skip static and final int modifiers = aField.getModifiers(); if ( Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) { continue; } // We skip the fields annotated with @Generated and @GeneratedValue if (aField.getAnnotation(GeneratedValue.class) == null && aField.getAnnotation(Generated.class) == null) { aField.setAccessible(true); Object value = aField.get(src); if (aField.getType().isPrimitive() || String.class == aField.getType() || Number.class.isAssignableFrom(aField.getType()) || Boolean.class == aField.getType() || Enum.class.isAssignableFrom(aField.getType())) { try { // TODO: You may want to recursive copy value too aField.set(dest, value); } catch(Exception e) { e.printStackTrace(); } } } } c = c.getSuperclass(); } return dest; } 

就像有些人已经建议我使用inheritance+浅层副本(共享引用)或使用库进行深度克隆来解决这个问题,这些库允许我排除/操纵自动生成的id(当你想要复制项目时)。

由于您不希望过多地弯曲数据库模型,因此请使用具有公共属性的Mapped Superclass 。 这根本不会反映在您的数据库中。 如果可以,我会使用单表inheritance ,它靠近您的模型(但可能需要对数据库层进行一些调整)。

 @MappedSuperclass public abstract class User { @Id @GeneratedValue private long id; // Common properties and relationships... 

然后让PermUserTempUser都inheritance自User ,这样它们就会有很多共同的状态:

 @Entity @Table(name="USER") public class PermUser extends User { // Specific properties } 

现在有几种可能的方法,如果你的类没有很多状态,你可以,例如,创建一个构建器,构建一个PermUser收集TempUsers列表的TempUsers

模拟代码:

 @Entity @Table(name="PERMANENT_USER") public class PermUser extends User { public PermUser() {} // default constructor public PermUser(List userData) { final Set f = new LinkedHashSet<>(); // don't set the id for(TempUser u : userData) { this.name = u.getName(); // Shallow copy that guarants uniqueness and insertion order // Favorite must override equals and hashCode f.addAll(u.getFavorites()); } this.favorites = new ArrayList<>(f); // Logic to conciliate dates } } 

当您持久PermUser ,它将生成一个新的id,级联的单向关系应该可以正常工作。

另一方面,如果你的类有很多属性和关系,而且在很多情况下你真的需要复制对象,那么你可以使用一个Bean映射库,比如Dozer (但要注意,克隆对象是一种代码味道)。

 Mapper mapper = new DozerBeanMapper(); mapper.map(tempUser.getFavorites(), user.getFavorites()); 

使用dozer,您可以通过注释 , API或XML配置Mappings,例如排除字段,类型转换等。

模拟映射:

  my.object.package.TempUser my.object.package.PermUser  

从纯粹的OO角度来看,实例从一种类型转换为另一种类型(Hibernate或不是Hibernate)并没有多大意义。 听起来您可能想要独立于其数据库表示重新考虑对象模型。 例如,FourWD看起来更像是汽车的属性,而不是专业化。

对此进行建模的一种好方法是创建类似于UserData类的内容,使TempUser具有UserDataPermUser具有UserData 。 你也可以让TempUser拥有一个PermUser ,尽管那不太清楚。 如果您的应用程序需要交替使用它们(您使用inheritance所获得的东西),那么这两个类都可以实现一个返回UserData的接口(或者在第二个选项getPermUserPermUser返回自己)。

如果您真的想要使用inheritance,最简单的方法可能是使用“每个类层次结构”映射它,然后使用直接JDBC直接更新鉴别器列。