JPA之Hibernate

JPA


定义:是 JavaEE 中一组用于持久化数据的 API,它提供了一种标准的 ORM 规范,用于 Java 对象映射到数据库中。
JPA 的开发是为了简化企业级应用程序的开发,降低应用程序与数据库之间的耦合度,并提高应用程序的可维护性和可扩展性。
JPA 定义了一系列注解和接口,用于描述 Java 对象与数据库表之间的映射关系。其中,最重要的是实体类(Entity)和实体管理器(EntityManager)。

注意:JPA 只是一种规范,具体的实现由不同的厂商提供,比如 Hibernate、EclipseLink 等。因此,在使用 JPA 时,需要选择一个合适的实现,并按照其提供的文档进行配置和使用。

Interview & Answer

什么是 JPA?它的作用是什么?

JPA(Java Persistence API)是 JavaEE 中一组用于持久化数据的 API,它提供了一种标准的 ORM(对象关系映射)规范,用于将 Java 对象映射到关系型数据库中。JPA 的作用是简化企业级应用程序的开发,降低应用程序与数据库之间的耦合度,并提高应用程序的可维护性和可扩展性。

JPA 中的实体类是什么?如何定义实体类?

实体类是一个普通的 Java 类,用于表示数据库中的一张表。在实体类中,使用注解来描述类与表之间的映射关系,比如 @Entity、@Table、@Id 等。实体类通常包含一组属性(属性对应表中的列),以及一些方法(用于操作这些属性)。

JPA 提供了哪些 API 接口?分别有什么作用?

JPA 提供了一些 API 接口,包括 EntityManager、EntityTransaction、Query 等,这些接口可以用来操作 Java 对象和数据库之间的映射关系。EntityManager 用于管理实体对象,EntityTransaction 用于管理事务,Query 用于执行查询操作。

JPA 中的实体管理器是什么?如何使用它来操作实体对象?

实体管理器是用于管理实体对象的类,它提供了一系列方法,用于对实体对象进行增删改查等操作。在 JPA 中,实体管理器是通过 EntityManagerFactory 创建的,每个应用程序通常只需要一个 EntityManagerFactory 实例,但可以创建多个 EntityManager 实例。可以通过调用 EntityManager 的方法来对实体对象进行操作,比如 persist、remove、merge、find 等方法。

JPA 中的注解有哪些?它们的作用是什么?

JPA 中的注解包括 @Entity、@Table、@Id、@Column 等,用于描述 Java 对象与数据库表之间的映射关系。其中,@Entity 用于指定一个 Java 类为实体类,@Table 用于指定实体类对应的数据库表,@Id 用于指定实体类属性为主键,@Column 用于指定实体类属性与数据库表列之间的映射关系。

JPA 中的事务管理是如何实现的?如何配置事务管理器?

JPA 中的事务管理可以通过实体管理器的 getTransaction 方法来获取一个 EntityTransaction 实例,然后调用其 begin、commit、rollback 等方法来管理事务。在 Spring 框架中,可以使用 @Transactional 注解来配置事务管理器,通过在方法或类上添加该注解来实现事务管理。

JPA 中的缓存机制是如何实现的?什么是一级缓存和二级缓存?

JPA 中的缓存机制包括一级缓存和二级缓存。一级缓存是指实体管理器与数据库之间的缓存,它是默认开启的,可以通过 EntityManager 的 clear、refresh 等方法来操作。二级缓存是指在多个实体管理器之间共享的缓存,它需要通过配置文件来启用,并且需要使用 JPA 提供商提供的缓存实现。二级缓存可以减少数据库访问次数,提高应用程序的性能。

JPA 中的 JPQL 是什么?如何使用 JPQL 进行查询操作?

JPQL(Java Persistence Query Language)是一种面向对象的查询语言,用于查询实体对象。JPQL 与 SQL 语句类似,但是它是面向对象的,可以直接引用实体类和实体类属性。JPQL 可以使用 EntityManager 的 createQuery 方法来创建查询对象,然后使用查询对象的 setParameters、getResultList 等方法来执行查询操作。

JPA 的实现有哪些?它们之间有什么区别?

JPA 的实现有很多,比较常见的有 Hibernate、EclipseLink、OpenJPA 等。这些实现都遵循 JPA 规范,但是在实现上有些许差异,比如性能、功能、稳定性等方面。开发人员可以根据自己的需求选择合适的实现。

JPA 和 Hibernate 之间有什么关系?它们之间有什么区别?

JPA 是一种规范,而 Hibernate 是实现了 JPA 规范的 ORM 框架。Hibernate 可以看作是 JPA 的一种实现,它实现了 JPA 规范,并提供了一些 JPA 规范之外的功能,比如缓存、批量操作、动态查询等。但是,使用 Hibernate 并不一定要遵循 JPA 规范,开发人员可以直接使用 Hibernate 提供的 API 来操作数据库。

JPA 实现方式及缓存默认状态

        JPA规范并没有指定一级和二级缓存的默认状态,这取决于具体的JPA实现和配置。下面是常见的JPA实现的默认缓存状态:

  • Hibernate:Hibernate是一个常用的JPA实现,它的一级缓存(Session级别的缓存)默认是开启的,而二级缓存(应用程序级别的缓存)默认是关闭的。可以通过配置文件或者代码的方式来开启或关闭二级缓存。

  • EclipseLink:EclipseLink是另一个常用的JPA实现,它的一级缓存(EntityManager级别的缓存)默认是开启的,而二级缓存(应用程序级别的缓存)默认是关闭的。可以通过配置文件或者代码的方式来开启或关闭二级缓存。

  • OpenJPA:OpenJPA是另一个流行的JPA实现,它的一级缓存(EntityManager级别的缓存)默认是开启的,而二级缓存(应用程序级别的缓存)默认是关闭的。可以通过配置文件或者代码的方式来开启或关闭二级缓存。

如何理解都默认开一级缓存

        有点类似都在一个事务中,这要求可重复读等,所以都要开启

JPA 注解使用 demo

    // 查询
    @Query(value = "SELECT * FROM user WHERE name = :name", nativeQuery = true)
    List<User> findByName(@Param("name") String name);

    // 修改
    @Modifying
    @Query(value = "UPDATE user SET age = :age WHERE id = :id", nativeQuery = true)
    void updateAgeById(@Param("id") Long id, @Param("age") Integer age);

    // 删除
    @Modifying
    @Query(value = "DELETE FROM user WHERE id = :id", nativeQuery = true)
    void deleteById(@Param("id") Long id);

    // list 入参
    @Query("SELECT u FROM User u WHERE u.name IN :names")
    List<User> findByNames(@Param("names") List<String> names);

    // map 入参
    // 在这个示例中,我们定义了一个自定义查询方法findByCondition,使用@Query注解指定了JPQL查询语句。查询语句中使用了实体类User和其属性name、age,使用AND关键字指定查找name属性等于condition中的"name"键对应的值,age属性大于等于condition中的"minAge"键对应的值的用户信息
    // 使用Map作为查询条件时,需要注意的是,Map中的键必须与JPQL查询语句中的参数名一致,否则会抛出异常。
    @Query("SELECT u FROM User u WHERE u.name = :name AND u.age >= :minAge")
    List<User> findByCondition(@Param("condition") Map<String, Object> condition);

    // 复杂对象入参
    // @Query("SELECT u FROM User u WHERE u.name = :#{#query.name} AND u.age >= :#{#query.minAge}")
    //    List findByCondition(@Param("query") UserQuery query);
    //
    //public class UserQuery {
    //    private String name;
    //    private Integer minAge;
    //    // 省略getter和setter方法
    //}

    // 联表查询
    // @Query("SELECT o FROM Order o JOIN o.user u WHERE u.name = :name")
    //    List findByUserName(@Param("name") String name);

    // null 不更新
    @Query("UPDATE TP_USER_ACTIVITY_LOG \n" +
        "SET IF :name IS NOT NULL THEN name = :name END IF\n" +
        ",age = :age\n" +
        "WHERE LOG_DATE = :logDate")
    Integer updateTest(@Param("name") String name, @Param("age") String age, @Param("logDate") Date logDate);

    // 关键在于数据库if 语句使用,不同数据库if的用法有所不同,所以,没有通用性
    
    // where 条件 为 null 
        //  如果为空时显示1=1 代表参数为真,对查询结果不产生作用。
        // "WHERE IF (:byname is not null, c.byname LIKE CONCAT('%',:byname,'%') , 1 = 1) and IF (:isMember is not null, c.is_member = :isMember , 1 = 1) and IF (:isBlacklist is not null, c.is_blacklist = :isBlacklist , 1 = 1) and "
    // + "IF (:phone is not null, c.phone = :phone , 1 = 1)"

Hibernate


Hibernate 一级缓存

Hibernate 的一级缓存是 Session 级别的缓存,它默认是开启的,并且通常是不建议关闭的。但是,在某些特定的场景下,关闭一级缓存可能是有必要的,例如:

  • 当需要强制刷新缓存,以避免缓存数据和数据库数据不一致时,可以考虑关闭一级缓存。
  • 当需要避免缓存数据的过期或者内存溢出时,可以考虑关闭一级缓存。
  • 当需要测试或者调试缓存相关的问题时,可以考虑关闭一级缓存。

关闭 Hibernate 的一级缓存有以下两种方式:

① 在 Session 中调用 clear() 方法:通过 Session 的 clear() 方法可以清空一级缓存中的所有数据。例如:

    Session session = sessionFactory.openSession();
    session.beginTransaction();
    // 这里执行数据库操作,将数据写入数据库
    session.clear(); // 清空一级缓存
    // 这里再次执行数据库操作,从数据库中读取最新的数据

② 在 Hibernate 配置文件中设置缓存策略:通过在 Hibernate 配置文件(如 hibernate.cfg.xml)中设置缓存策略可以关闭一级缓存。例如:

<hibernate-configuration>
<session-factory>
    
      <property name="hibernate.cache.use_second_level_cache">falseproperty>
      <property name="hibernate.cache.use_query_cache">falseproperty>
    
   session-factory>
hibernate-configuration>

在上述配置中,将 hibernate.cache.use_second_level_cache 和 hibernate.cache.use_query_cache 都设置为 false,可以关闭一级缓存和查询缓存。

注意:关闭一级缓存可能会导致性能下降和数据不一致等问题,因此在实际应用中需要慎重考虑是否关闭一级缓存。

Hibernate 一级缓存过期

        Hibernate 的一级缓存是 Session 级别的缓存,它存储的是 Session 中查询的实体对象。一级缓存的过期时间是根据缓存中的对象状态和 Session 的状态来判断的。一级缓存的过期包括以下几种情况:

  • 对象状态为持久化状态:当Session中的实体对象状态为持久化状态时,即该对象已经被保存到数据库中并且与数据库中的数据保持一致,那么该对象会一直保留在一级缓存中,直到Session关闭或者显式地从缓存中清除。

  • 对象状态为游离状态:当Session中的实体对象状态为游离状态时,即该对象已经被从Session中分离出来,与数据库中的数据不再保持一致,那么该对象会失效并从一级缓存中移除。

  • 对象状态为脱管状态:当Session中的实体对象状态为脱管状态时,即该对象已经被Session关闭或者从Session中分离出来,那么该对象会失效并从一级缓存中移除。

注意:一级缓存中的对象生命周期与 Session 的生命周期相关联。当 Session 关闭时,所有在一级缓存中的对象都会失效并从缓存中移除。因此,在使用 Hibernate 的过程中,需要根据具体的业务需求和场景来管理缓存,以避免数据的不一致和缓存的内存溢出等问题。

分布式 Hibernate 二级缓存一致性

        在分布式系统中,由于存在多个应用程序实例和多个数据库实例,使用Hibernate的二级缓存可能会导致数据的不一致性问题。为了保持Hibernate的二级缓存一致性,可以采用以下几种方法:

  • 配置缓存同步策略:通过配置缓存同步策略,可以保证多个应用程序实例之间的缓存数据一致性。例如,可以使用JGroups、ZooKeeper等工具来实现缓存同步和协调。

  • 配置缓存失效策略:通过配置缓存失效策略,可以及时使缓存数据失效,避免缓存数据和数据库数据不一致。例如,可以使用时间戳、版本号等方式来实现缓存数据的失效和更新。

  • 选择合适的二级缓存实现:不同的二级缓存实现具有不同的特点和优缺点,需要根据具体的业务需求和场景来选择合适的缓存实现。例如,Ehcache、Redis等缓存工具都是常用的二级缓存实现,它们都具有不同的特点和适用场景。

  • 避免缓存数据过期:在使用Hibernate的二级缓存时,需要注意缓存数据的过期时间,避免缓存数据过期导致数据不一致。需要根据具体的业务需求和场景来设置缓存数据的过期时间,以保证数据的一致性和有效性。

注意:保持 Hibernate 的二级缓存一致性是一个复杂的问题,需要综合考虑多个因素和技术手段。在实际应用中,需要根据具体的业务需求和场景来选择合适的缓存策略和技术,以保证系统的性能和可靠性。

Springboot 接入 Hibernate

  1. maven 依赖
<dependency>
  <groupId>org.springframework.bootgroupId>
  <artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
  <groupId>org.hibernategroupId>
  <artifactId>hibernate-coreartifactId>
  <version>${hibernate.version}version>
dependency>
  1. yaml 配置
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
  1. 创建 Entity
@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "age")
    private int age;

    // getters and setters
}
  1. 创建 Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByName(String name);
}
  1. 调用
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    public List<User> findByName(String name) {
        return userRepository.findByName(name);
    }
}

你可能感兴趣的:(hibernate)