Spring Repository问题

时间:2015-05-01 14:32:08

标签: spring jpa spring-data

我似乎对JPA Repositories如何工作感到困惑。 在坚果壳

@Entity
public class User extends AbstractEntity {
    protected final static String FK_NAME = "USER_ID";

    @Column(nullable = false)
    private String firstName;

    @OneToMany(cascade = ALL, fetch = FetchType.LAZY, orphanRemoval = true)
    @JoinColumn(name = "userId")
    private List<Detail> details = new ArrayList<Detail>();
}

@Entity
public class Detail extends AbstractEntity {
    Long userId;

    String hello;
}

@Repository
public interface UserRepository extends CrudRepository<User, Long> {

     User findByFirstName(@Param("firstName") String firstName);
}

这是应用程序中唯一的控制器:

@RestController
public class Home {
    @Autowired
    UserRepository userRepository;

    @Autowired
    DetailsRepository loanRepository;

    @RequestMapping(value = "")
    public HttpEntity home() {
        User user = userRepository.findByFirstName("John");

        if (user == null) {
            user = new User();
            user.setFirstName("John");
        }

        Detail detail = new Detail();
        detail.setHello("Hello Msh");

        user.getDetails().add(detail);

        userRepository.save(user);

        return new ResponseEntity("hi", HttpStatus.OK);
    }
}

在调试会话的屏幕截图下方,应用程序刚刚启动,并且对home()方法的get请求创建了新用户,新细节,为用户添加了详细信息。 New user is created

下面的示例 - 保存用户后,详细信息实体会更新 New user is saved

现在,在下一个请求中,找到旧用户John并添加了一个新的详细实例。 Old user was found

旧用户已保存,但现在新创建的详细信息无法在外部更新。 Old user was updated and saves

为什么这只能第一次起作用?

2 个答案:

答案 0 :(得分:1)

基本上这么多失败了,所以我建议你倒退一步。如果你是wana,那么就找到解决这个问题的简短方法继续阅读;)

第一部分与Jaiwo99的答案有关: 正如我在intellij的gradle视图中看到的那样,你使用的是Spring Boot。因此,有必要在配置类之上放置@EnableTransactionManagement。否则@Transacion注释没有任何效果。

第二部分是您的JPA / Hibernate模型映射。网上有这么多不好的做法,难怪大多数初学者都会遇到麻烦。

正确的版本可能看起来像(未经测试)

@Entity
public class User extends AbstractEntity {

    @Column(nullable = false)
    private String firstName;

    @OneToMany(cascade = ALL, fetch = FetchType.LAZY, orphanRemoval = true, mappedBy="user")
    private List<Detail> details = new ArrayList<Detail>();

    public void addDetail(Detail detail) {
        details.add(detail);
        detail.setUser(user);
    }
}

@Entity
public class Detail extends AbstractEntity {
    @ManyToOne
    private User user;

    private String hello;

    public void setUser(User user){
        this.user = user;
    }
}

与创建模型映射相关的一些一般性建议:

  • 尽可能避免双向映射
  • 级联是在服务级别而不是在模型级别做出的决定,并且可能具有巨大的缺点。所以初学者要避免它。
  • 我不知道为什么人们喜欢将JoinColumn,JoinTable和任何连接注释放在字段之上。这样做的唯一原因是你有遗留数据库(我的意见)。如果您不喜欢jpa提供程序创建的名称,请提供不同的命名策略。
  • 我会为用户类提供自定义名称,因为这在某些数据库中是保留字。

答案 1 :(得分:0)

很简单,第一次在hibernate会话之外保存一个新实体,第二次,你得到的用户对象是一个分离的对象,默认情况下hibernate不会认为它在这种情况下被改变了。

*解决方案* 将此逻辑移动到另一个使用@transactional注释的服务类 要么 使用事务性注释您的控制器 要么 在用户类上覆盖equals和hashCode方法也可以提供帮助