jpa和hibernate_JPA和Hibernate级联类型的初学者指南

jpa和hibernate

介绍

JPA实体状态转换转换为数据库DML语句。 由于对实体图进行操作很常见,因此JPA允许我们将实体状态更改从父级传播到子级

通过CascadeType映射配置此行为。

JPA与Hibernate级联类型

Hibernate支持所有JPA级联类型和一些其他旧式级联样式。 下表绘制了JPA级联类型与其等效的Hibernate本机API之间的关联:

JPA EntityManager操作 JPA CascadeType Hibernate本机会话操作 Hibernate原生CascadeType 事件监听器
分离(实体) 分离 逐出(实体) 分离

EVICT
默认驱逐事件侦听器
合并(实体) 合并 合并(实体) 合并 默认合并事件监听器
坚持(实体) 坚持 坚持(实体) 坚持 默认的持久事件监听器
刷新(实体) 刷新 刷新(实体) 刷新 默认刷新事件监听器
删除(实体) 去掉 删除(实体) 删除删除 默认删除事件监听器
saveOrUpdate(实体) SAVE_UPDATE 默认的保存或更新事件监听器
复制(实体,复制模式) 复制 默认复制事件监听器
锁(实体,lockModeType) buildLockRequest(实体,lockOptions) 默认锁定事件监听器
以上所有EntityManager方法 所有 以上所有的Hibernate Session方法 所有

从该表可以得出以下结论:

  • 在JPA EntityManager或Hibernate Session上调用persistmergerefresh没有什么区别。
  • JPA的removedetach调用被委托给Hibernate Delete逐出本机操作。
  • 只有Hibernate支持复制saveOrUpdate 。 尽管复制对于某些非常特定的情况很有用(当确切的实体状态需要在两个不同的数据源之间进行镜像时),但持久 合并合并始终是比本机saveOrUpdate操作更好的替代方法。将持久对象用于TRANSIENT实体,并为已分离实体进行合并。saveOrUpdate的缺点(将分离的实体快照传递给已经管理该实体的会话时 )导致了合并操作的前身:现已不存在的saveOrUpdateCopy操作。
  • JPA锁定方法与Hibernate锁定请求方法具有相同的行为。
  • JPA CascadeType.ALL不仅适用于EntityManager状态更改操作,而且还适用于所有Hibernate CascadeTypes 。因此,如果将关联与CascadeType.ALL映射,您仍然可以级联Hibernate特定事件。 例如,即使JPA没有定义LOCK CascadeType ,您也可以级联JPA锁定操作(尽管它的行为就像是重新附加,而不是实际的锁定请求传播)。

级联最佳做法

级联仅对父级 - 子级关联有意义( 父级实体状态转换级联到其子级实体)。 从孩子级联到父级不是很有用,通常,这是映射代码的味道。

接下来,我将采取分析所有JPA 家长的级联行为- 关联。

一对一

最常见的一对一双向关联如下所示:

@Entity
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    @OneToOne(mappedBy = "post",
        cascade = CascadeType.ALL, orphanRemoval = true)
    private PostDetails details;

    public Long getId() {
        return id;
    }

    public PostDetails getDetails() {
        return details;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void addDetails(PostDetails details) {
        this.details = details;
        details.setPost(this);
    }

    public void removeDetails() {
        if (details != null) {
            details.setPost(null);
        }
        this.details = null;
    }
}

@Entity
public class PostDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "created_on")
    @Temporal(TemporalType.TIMESTAMP)
    private Date createdOn = new Date();

    private boolean visible;

    @OneToOne
    @PrimaryKeyJoinColumn
    private Post post;

    public Long getId() {
        return id;
    }

    public void setVisible(boolean visible) {
        this.visible = visible;
    }

    public void setPost(Post post) {
        this.post = post;
    }
}

Post实体扮演Parent角色,而PostDetailsChild

双向关联应始终在两侧进行更新,因此父级侧应包含addChildremoveChild组合。 这些方法确保我们始终同步关联的双方,以避免对象或关系数据损坏问题。

在这种特殊情况下, CascadeType.ALL和孤立删除是有意义的,因为PostDetails生命周期绑定到其Post Parent实体的生命周期。

进行一对一的持久化操作

CascadeType.PERSISTCascadeType.ALL配置一起提供,因此我们仅需持久化Post实体,并且关联的PostDetails实体也将持久化:

Post post = new Post();
post.setName("Hibernate Master Class");

PostDetails details = new PostDetails();

post.addDetails(details);

session.persist(post);

生成以下输出:

INSERT INTO post(id, NAME) 
VALUES (DEFAULT, Hibernate Master Class'')

insert into PostDetails (id, created_on, visible) 
values (default, '2015-03-03 10:17:19.14', false)

级联一对一合并操作

CascadeType.MERGE继承自CascadeType.ALL设置,因此我们只需要合并Post实体,并且关联的PostDetails也将合并:

Post post = newPost();
post.setName("Hibernate Master Class Training Material");
post.getDetails().setVisible(true);

doInTransaction(session -> {
    session.merge(post);
});

合并操作生成以下输出:

SELECT onetooneca0_.id     AS id1_3_1_,
   onetooneca0_.NAME       AS name2_3_1_,
   onetooneca1_.id         AS id1_4_0_,
   onetooneca1_.created_on AS created_2_4_0_,
   onetooneca1_.visible    AS visible3_4_0_
FROM   post onetooneca0_
LEFT OUTER JOIN postdetails onetooneca1_ 
    ON onetooneca0_.id = onetooneca1_.id
WHERE  onetooneca0_.id = 1

UPDATE postdetails SET 
    created_on = '2015-03-03 10:20:53.874', visible = true 
WHERE  id = 1

UPDATE post SET 
    NAME = 'Hibernate Master Class Training Material' 
WHERE  id = 1

级联一对一删除操作

CascadeType.REMOVE也继承自CascadeType.ALL配置,因此Post实体的删除也会触发PostDetails实体的删除:

Post post = newPost();

doInTransaction(session -> {
    session.delete(post);
});

生成以下输出:

delete from PostDetails where id = 1
delete from Post where id = 1

一对一删除孤立级联操作

如果一个孩子实体从母公司分离,儿童外键设置为NULL。 如果我们也要删除“ 行”,则必须使用孤立删除支持。

doInTransaction(session -> {
    Post post = (Post) session.get(Post.class, 1L);
    post.removeDetails();
});

除去孤儿将生成以下输出:

SELECT onetooneca0_.id         AS id1_3_0_,
       onetooneca0_.NAME       AS name2_3_0_,
       onetooneca1_.id         AS id1_4_1_,
       onetooneca1_.created_on AS created_2_4_1_,
       onetooneca1_.visible    AS visible3_4_1_
FROM   post onetooneca0_
LEFT OUTER JOIN postdetails onetooneca1_
    ON onetooneca0_.id = onetooneca1_.id
WHERE  onetooneca0_.id = 1

delete from PostDetails where id = 1

单向一对一关联

大多数情况下, 实体是反方(如的mappedBy), 儿童 controling通过它的外键关联。 但是级联不限于双向关联,我们还可以将其用于单向关系:

@Entity
public class Commit {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String comment;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinTable(
        name = "Branch_Merge_Commit",
        joinColumns = @JoinColumn(
            name = "commit_id", 
            referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(
            name = "branch_merge_id", 
            referencedColumnName = "id")
    )
    private BranchMerge branchMerge;

    public Commit() {
    }

    public Commit(String comment) {
        this.comment = comment;
    }

    public Long getId() {
        return id;
    }

    public void addBranchMerge(
        String fromBranch, String toBranch) {
        this.branchMerge = new BranchMerge(
             fromBranch, toBranch);
    }

    public void removeBranchMerge() {
        this.branchMerge = null;
    }
}

@Entity
public class BranchMerge {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String fromBranch;

    private String toBranch;

    public BranchMerge() {
    }

    public BranchMerge(
        String fromBranch, String toBranch) {
        this.fromBranch = fromBranch;
        this.toBranch = toBranch;
    }

    public Long getId() {
        return id;
    }
}

层叠在于传播实体状态过渡到一个或多个儿童的实体,它可用于单向和双向关联。

一对多

最常见的 - 关联由一到多和多到一的关系,其中级联是只对一个一对多侧有用:

@Entity
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    @OneToMany(cascade = CascadeType.ALL, 
        mappedBy = "post", orphanRemoval = true)
    private List<Comment> comments = new ArrayList<>();

    public void setName(String name) {
        this.name = name;
    }

    public List<Comment> getComments() {
        return comments;
    }

    public void addComment(Comment comment) {
        comments.add(comment);
        comment.setPost(this);
    }

    public void removeComment(Comment comment) {
        comment.setPost(null);
        this.comments.remove(comment);
    }
}

@Entity
public class Comment {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne
    private Post post;

    private String review;

    public void setPost(Post post) {
        this.post = post;
    }

    public String getReview() {
        return review;
    }

    public void setReview(String review) {
        this.review = review;
    }
}

像一对一示例一样, CascadeType.ALL和孤立删除是合适的,因为Comment生命周期绑定到其Post Parent实体的生命周期。

级联一对多持久化操作

我们只需保留Post实体,所有相关的Comment实体也将保留:

Post post = new Post();
post.setName("Hibernate Master Class");

Comment comment1 = new Comment();
comment1.setReview("Good post!");
Comment comment2 = new Comment();
comment2.setReview("Nice post!");

post.addComment(comment1);
post.addComment(comment2);

session.persist(post);

持久操作将生成以下输出:

insert into Post (id, name) 
values (default, 'Hibernate Master Class')

insert into Comment (id, post_id, review) 
values (default, 1, 'Good post!')

insert into Comment (id, post_id, review) 
values (default, 1, 'Nice post!')

级联一对多合并操作

合并Post实体也将合并所有Comment实体:

Post post = newPost();
post.setName("Hibernate Master Class Training Material");

post.getComments()
    .stream()
    .filter(comment -> comment.getReview().toLowerCase()
         .contains("nice"))
    .findAny()
    .ifPresent(comment -> 
        comment.setReview("Keep up the good work!")
);

doInTransaction(session -> {
    session.merge(post);
});

生成以下输出:

SELECT onetomanyc0_.id    AS id1_1_1_,
       onetomanyc0_.NAME  AS name2_1_1_,
       comments1_.post_id AS post_id3_1_3_,
       comments1_.id      AS id1_0_3_,
       comments1_.id      AS id1_0_0_,
       comments1_.post_id AS post_id3_0_0_,
       comments1_.review  AS review2_0_0_
FROM   post onetomanyc0_
LEFT OUTER JOIN comment comments1_
    ON onetomanyc0_.id = comments1_.post_id
WHERE  onetomanyc0_.id = 1

update Post set 
    name = 'Hibernate Master Class Training Material' 
where id = 1

update Comment set 
    post_id = 1, 
    review='Keep up the good work!' 
where id = 2

级联一对多删除操作

删除Post实体后,关联的Comment实体也将被删除:

Post post = newPost();

doInTransaction(session -> {
    session.delete(post);
});

生成以下输出:

delete from Comment where id = 1
delete from Comment where id = 2
delete from Post where id = 1

一对多删除孤立级联操作

移除孤儿使我们可以在其父级不再引用子级实体时将其删除:

newPost();

doInTransaction(session -> {
    Post post = (Post) session.createQuery(
        "select p " +
                "from Post p " +
                "join fetch p.comments " +
                "where p.id = :id")
        .setParameter("id", 1L)
        .uniqueResult();
    post.removeComment(post.getComments().get(0));
});

正如我们在以下输出中看到的,评论已删除:

SELECT onetomanyc0_.id    AS id1_1_0_,
       comments1_.id      AS id1_0_1_,
       onetomanyc0_.NAME  AS name2_1_0_,
       comments1_.post_id AS post_id3_0_1_,
       comments1_.review  AS review2_0_1_,
       comments1_.post_id AS post_id3_1_0__,
       comments1_.id      AS id1_0_0__
FROM   post onetomanyc0_
INNER JOIN comment comments1_
    ON onetomanyc0_.id = comments1_.post_id
WHERE  onetomanyc0_.id = 1

delete from Comment where id = 1

多对多

多对多关系非常棘手,因为此关联的每一方都扮演“ 父母”和“ 孩子”角色。 尽管如此,我们仍可以从一侧传播我们想要传播实体状态变化的位置。

我们不应该默认使用CascadeType.ALL ,因为CascadeTpe.REMOVE最终可能会删除比我们期望的更多的东西(您很快就会发现):

@Entity
public class Author {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @Column(name = "full_name", nullable = false)
    private String fullName;

    @ManyToMany(mappedBy = "authors", 
        cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    private List<Book> books = new ArrayList<>();

    private Author() {}

    public Author(String fullName) {
        this.fullName = fullName;
    }

    public Long getId() {
        return id;
    }

    public void addBook(Book book) {
        books.add(book);
        book.authors.add(this);
    }

    public void removeBook(Book book) {
        books.remove(book);
        book.authors.remove(this);
    }

    public void remove() {
        for(Book book : new ArrayList<>(books)) {
            removeBook(book);
        }
    }
}

@Entity
public class Book {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @Column(name = "title", nullable = false)
    private String title;

    @ManyToMany(cascade = 
        {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(name = "Book_Author",
        joinColumns = {
            @JoinColumn(
                name = "book_id", 
                referencedColumnName = "id"
            )
        },
        inverseJoinColumns = {
            @JoinColumn(
                name = "author_id", 
                referencedColumnName = "id"
            )
        }
    )
    private List<Author> authors = new ArrayList<>();

    private Book() {}

    public Book(String title) {
        this.title = title;
    }
}

级联多对多持久操作

坚持作者实体也将保留书籍

Author _John_Smith = new Author("John Smith");
Author _Michelle_Diangello = 
    new Author("Michelle Diangello");
Author _Mark_Armstrong = 
    new Author("Mark Armstrong");

Book _Day_Dreaming = new Book("Day Dreaming");
Book _Day_Dreaming_2nd = 
    new Book("Day Dreaming, Second Edition");

_John_Smith.addBook(_Day_Dreaming);
_Michelle_Diangello.addBook(_Day_Dreaming);

_John_Smith.addBook(_Day_Dreaming_2nd);
_Michelle_Diangello.addBook(_Day_Dreaming_2nd);
_Mark_Armstrong.addBook(_Day_Dreaming_2nd);

session.persist(_John_Smith);
session.persist(_Michelle_Diangello);
session.persist(_Mark_Armstrong);

BookBook_Author行与Authors一起插入:

insert into Author (id, full_name) 
values (default, 'John Smith')

insert into Book (id, title) 
values (default, 'Day Dreaming')

insert into Author (id, full_name) 
values (default, 'Michelle Diangello')

insert into Book (id, title) 
values (default, 'Day Dreaming, Second Edition')

insert into Author (id, full_name) 
values (default, 'Mark Armstrong')

insert into Book_Author (book_id, author_id) values (1, 1)
insert into Book_Author (book_id, author_id) values (1, 2)
insert into Book_Author (book_id, author_id) values (2, 1)
insert into Book_Author (book_id, author_id) values (2, 2)
insert into Book_Author (book_id, author_id) values (3, 1)

解除多对多关联的一侧

要删除Author ,我们需要取消关联属于可移动实体的所有Book_Author关系:

doInTransaction(session -> {
    Author _Mark_Armstrong =
        getByName(session, "Mark Armstrong");
    _Mark_Armstrong.remove();
    session.delete(_Mark_Armstrong);
});

该用例生成以下输出:

SELECT manytomany0_.id        AS id1_0_0_,
       manytomany2_.id        AS id1_1_1_,
       manytomany0_.full_name AS full_nam2_0_0_,
       manytomany2_.title     AS title2_1_1_,
       books1_.author_id      AS author_i2_0_0__,
       books1_.book_id        AS book_id1_2_0__
FROM   author manytomany0_
INNER JOIN book_author books1_
	ON manytomany0_.id = books1_.author_id
INNER JOIN book manytomany2_
	ON books1_.book_id = manytomany2_.id
WHERE  manytomany0_.full_name = 'Mark Armstrong'

SELECT books0_.author_id  AS author_i2_0_0_,
       books0_.book_id    AS book_id1_2_0_,
       manytomany1_.id    AS id1_1_1_,
       manytomany1_.title AS title2_1_1_
FROM   book_author books0_
INNER JOIN book manytomany1_
    ON books0_.book_id = manytomany1_.id
WHERE  books0_.author_id = 2

delete from Book_Author where book_id = 2

insert into Book_Author (book_id, author_id) values (2, 1)
insert into Book_Author (book_id, author_id) values (2, 2)

delete from Author where id = 3

多对多关联会生成太多冗余SQL语句,并且经常很难调整它们。 接下来,我将演示多对多CascadeType.REMOVE隐藏的危险。

多对多CascadeType.REMOVE陷阱

多对多CascadeType.ALL是另一个代码异味,我在查看代码时经常碰到。 所述CascadeType.REMOVE使用CascadeType.ALL时自动继承,但实体去除不仅应用到链接表,但对关联的另一侧为好。

让我们将Author实体书籍多对多关联更改为使用CascadeType.ALL代替:

@ManyToMany(mappedBy = "authors", 
    cascade = CascadeType.ALL)
private List<Book> books = new ArrayList<>();

删除一位作者时

doInTransaction(session -> {
    Author _Mark_Armstrong = 
        getByName(session, "Mark Armstrong");
    session.delete(_Mark_Armstrong);
    Author _John_Smith = 
        getByName(session, "John Smith");
    assertEquals(1, _John_Smith.books.size());
});

属于已删除作者的所有图书都将被删除,即使我们仍与已删除图书相关联的其他作者也是如此:

SELECT manytomany0_.id        AS id1_0_,
       manytomany0_.full_name AS full_nam2_0_
FROM   author manytomany0_
WHERE  manytomany0_.full_name = 'Mark Armstrong'  

SELECT books0_.author_id  AS author_i2_0_0_,
       books0_.book_id    AS book_id1_2_0_,
       manytomany1_.id    AS id1_1_1_,
       manytomany1_.title AS title2_1_1_
FROM   book_author books0_
INNER JOIN book manytomany1_ ON 
       books0_.book_id = manytomany1_.id
WHERE  books0_.author_id = 3  

delete from Book_Author where book_id=2
delete from Book where id=2
delete from Author where id=3

通常,此行为与业务逻辑期望不符,仅在首次删除实体时才发现。

如果我们也将CascadeType.ALL设置为Book实体,则可以进一步推动该问题:

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "Book_Author",
    joinColumns = {
        @JoinColumn(
            name = "book_id", 
            referencedColumnName = "id"
        )
    },
    inverseJoinColumns = {
        @JoinColumn(
            name = "author_id", 
            referencedColumnName = "id"
        )
    }
)

这次,不仅书籍被删除,而且作者也被删除:

doInTransaction(session -> {
    Author _Mark_Armstrong = 
        getByName(session, "Mark Armstrong");
    session.delete(_Mark_Armstrong);
    Author _John_Smith = 
        getByName(session, "John Smith");
    assertNull(_John_Smith);
});

作者的删除触发所有相关书籍的删除,这进一步触发所有相关的作者的删除。 这是一个非常危险的操作,会导致大规模实体删除,这很少是预期的行为。

SELECT manytomany0_.id        AS id1_0_,
       manytomany0_.full_name AS full_nam2_0_
FROM   author manytomany0_
WHERE  manytomany0_.full_name = 'Mark Armstrong'  

SELECT books0_.author_id  AS author_i2_0_0_,
       books0_.book_id    AS book_id1_2_0_,
       manytomany1_.id    AS id1_1_1_,
       manytomany1_.title AS title2_1_1_
FROM   book_author books0_
INNER JOIN book manytomany1_
   ON books0_.book_id = manytomany1_.id
WHERE  books0_.author_id = 3  

SELECT authors0_.book_id      AS book_id1_1_0_,
       authors0_.author_id    AS author_i2_2_0_,
       manytomany1_.id        AS id1_0_1_,
       manytomany1_.full_name AS full_nam2_0_1_
FROM   book_author authors0_
INNER JOIN author manytomany1_
   ON authors0_.author_id = manytomany1_.id
WHERE  authors0_.book_id = 2  

SELECT books0_.author_id  AS author_i2_0_0_,
       books0_.book_id    AS book_id1_2_0_,
       manytomany1_.id    AS id1_1_1_,
       manytomany1_.title AS title2_1_1_
FROM   book_author books0_
INNER JOIN book manytomany1_
   ON books0_.book_id = manytomany1_.id
WHERE  books0_.author_id = 1 

SELECT authors0_.book_id      AS book_id1_1_0_,
       authors0_.author_id    AS author_i2_2_0_,
       manytomany1_.id        AS id1_0_1_,
       manytomany1_.full_name AS full_nam2_0_1_
FROM   book_author authors0_
INNER JOIN author manytomany1_
   ON authors0_.author_id = manytomany1_.id
WHERE  authors0_.book_id = 1  

SELECT books0_.author_id  AS author_i2_0_0_,
       books0_.book_id    AS book_id1_2_0_,
       manytomany1_.id    AS id1_1_1_,
       manytomany1_.title AS title2_1_1_
FROM   book_author books0_
INNER JOIN book manytomany1_
   ON books0_.book_id = manytomany1_.id
WHERE  books0_.author_id = 2  

delete from Book_Author where book_id=2
delete from Book_Author where book_id=1
delete from Author where id=2
delete from Book where id=1
delete from Author where id=1 
delete from Book where id=2
delete from Author where id=3

这种用例在很多方面都是错误的。 大量不必要的SELECT语句,最终我们最终删除了所有作者及其所有书籍。 这就是为什么当您在多对多关联中发现CascadeType.ALL时,它应该引起您的注意。

当涉及到Hibernate映射时,您应该始终追求简单性。 Hibernate文档也证实了这一假设:

真正的多对多关联的实际测试案例很少见。 大多数时候,您需要存储在“链接表”中的其他信息。 在这种情况下,最好将两个一对多关联用于中间链接类。 实际上,大多数关联是一对多和多对一的。 因此,在使用任何其他关联样式时,您应谨慎进行。

结论

级联是一种方便的ORM功能,但并非没有问题。 您应该仅从父级实体级联到子级,而不是相反。 您应该始终仅使用业务逻辑要求所要求的Casacde操作,而不应将CascadeType.ALL转换为默认的Parent-Child关联实体状态传播配置。

翻译自: https://www.javacodegeeks.com/2015/03/a-beginners-guide-to-jpa-and-hibernate-cascade-types.html

jpa和hibernate

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JPAJava Persistence API)和Hibernate是两种在Java平台上用于对象关系映射(ORM)的技术。 JPAJava EE规范中定义的一套ORM接口,它为开发人员提供了一种标准的方式来访问持久化数据。而Hibernate是一个实现了JPA规范的ORM框架,它提供了强大的对象关系映射功能以及其他与持久化相关的特性。 以下是JPAHibernate之间的一些区别: 1. 概念层面:JPA是一种规范,而Hibernate是实现该规范的一个框架。 2. 灵活性:JPA是一种标准化的接口,允许开发人员在不同的JPA实现之间进行切换。因此,使用JPA可以更容易地切换到其他ORM框架,而不仅仅局限于Hibernate。 3. 学习曲线:由于JPA是一个较为抽象的接口,学习和使用JPA可能需要更多时间和学习成本。相比之下,Hibernate提供了更具体和易于理解的API,并且有更多的文档和教程可供参考。 4. 生态系统:Hibernate是一个成熟且广泛使用的ORM框架,拥有强大的社区支持和丰富的生态系统。它提供了丰富的功能和性能优化选项。虽然JPA也有一些实现,但Hibernate被认为是JPA最常用和最流行的实现之一。 总结起来,JPAJava EE规范中定义的一套ORM接口,而Hibernate是一个实现了JPA规范的强大ORM框架。通过使用JPA,开发人员可以获得更高的灵活性和可移植性,而Hibernate则提供了更具体和易于使用的API以及丰富的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值