Spring存储库问题

Spring存储库问题,spring,jpa,spring-data,Spring,Jpa,Spring Data,我似乎对JPA存储库应该如何工作感到困惑。 在坚果壳里 @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,

我似乎对JPA存储库应该如何工作感到困惑。 在坚果壳里

@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);
}
下面是调试会话的屏幕截图,其中应用程序刚刚启动,get request to home()方法创建新用户、新详细信息,并向用户添加详细信息。

下面的示例-保存用户时,详细信息实体将更新

现在在下一个请求中,找到了老用户John,并添加了一个新的详细信息实例。

旧用户已保存,但新创建的详细信息不会在外部更新。


<> P>为什么这只是第一次工作?

非常简单,第一次你在Hibernate会话之外保存了一个新的实体,第二次,你得到的用户对象是一个分离的对象,默认情况下Hibernate不会认为在这种情况下被改变了。p> *解决方案* 将此逻辑移动到另一个服务类,该服务类用@transactional注释 或 用事务性注释控制器 或


用户类上的Override equals和hashCode方法也可能有帮助

基本上,有太多的失败发生,所以我建议您后退一步。如果你想找到解决这个问题的方法,继续阅读;)

第一部分与Jaiwo99的答案相关: 正如我在intellij的gradle视图中看到的,您正在使用Spring Boot。因此,有必要将@EnableTransactionManagement放在配置类的顶部。否则,@Transacion注释将不起任何作用

第二部分是JPA/Hibernate模型映射。网上有这么多不好的练习,难怪大多数初学者在开始时都有困难

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

@实体
公共类用户扩展抽象实体{
@列(nullable=false)
私有字符串名;
@OneToMany(cascade=ALL,fetch=FetchType.LAZY,orphanRemoving=true,mappedBy=“user”)
私有列表详细信息=新建ArrayList();
公共无效添加详细信息(详细信息){
详细信息。添加(详细信息);
detail.setUser(用户);
}
}
@实体
公共类细节扩展了抽象实体{
@许多酮
私人用户;
私人字符串你好;
公共void setUser(用户){
this.user=用户;
}
}
有关创建模型映射的一些一般建议:

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

我不知道你在问什么。您希望这段代码做什么,它会做什么。请注意,您不应该有一个详细的
userId
字段,因为此列由OneToMany关联处理。@JBNizet我在问-为什么在创建新用户时,向User.details添加一个新的详细信息,然后保存新创建的用户-详细信息会用一个ID更新,但当我从数据库中获取创建的用户时,创建新的详细信息,并将该详细信息添加到用户,然后再次保存用户-user.Detail列表中的详细信息实例会更新,但传递给用户的实例不会更新。为什么它只在第一次工作。您是否提交了事务并查看了详细信息是否用ID保存在数据库中?我使用的是内存中的hsqldb。至少当我通过存储库获取它们时,它们就在那里了。您的JPA实现确实如此。如果它没有运行它,那是因为它不正确,所以请修复它。你只需要学习你正在使用的工具。它们不会按照您认为应该的方式工作。我尝试过使用javax.transaction.Transactional或org.springframework.transaction.annotation.Transactional进行注释,但仍然得到相同的结果。。。其实很奇怪,谢谢。还有一件事-假设
详细信息
实体没有对
用户
实体的反向引用。单向映射示例[此处]()连接列创建了一个附加列,这样当您有
电话时,您仍然可以查询
员工。如果使用spring数据“@Query”,我会怎么做?因为我已经尝试了EverythSign,但是失败了,因为@EntityI上没有物理属性。我会从用户中删除对细节的引用。在这种情况下,您也可以避免n+1选择问题。除此之外,还可以使用类似以下内容的查询“从用户u中选择u,其中:u.details的详细成员”。问题解决后,请将消息标记为正确。
@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);
    }
}
@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;
    }
}