Skip to main content

hibernate&jpa

· 13 min read
ayanami

jdbc: java database connectivity

jdbc 要先加载驱动,由各个数据库实现

jpa 通过 orm 框架生成 sql,再经过 jdbc 操作数据库

getBean 方法:

  • getBeanApplicationContext 接口中的一个方法,用于从 Spring 的 IoC 容器中显式地获取 Bean 实例。

  • 它通常在需要手动获取 Bean 时使用,比如在非 Spring 管理的类中或者在某些特定的场景下,你想要直接从容器中获取 Bean 而不是通过注入。

  • 使用 getBean 方法时,你需要知道 Bean 的名称或类型,并在调用时指定这些信息。

  • 示例代码:

ApplicationContext context = ...; // 获取ApplicationContext实例 MyBean myBean = context.getBean(MyBean.class); // 通过类型获取Bean


hibernate orm 框架,jpql 以及 JpaRepository

lombok 也很常用,@Data @NoArgsConstructor

Spring Data

统一和简化不同类型的持久化存储(Relational & noSQL)的访问

JPA 通过面向对象方式和 ORM 框架生成 SQL

JPA: java persistence API, 是一种 ORM 规范

该规范提供了:

- ORM 映射元数据, XML/注解,例如@Table, @Id, @Column
- JPA 的 API,用于操作实体对象 CRUD
- JPQL 查询语言(面向对象)

hibernate 是 JPA 规范的一种实现

> As we discussed in an earlier article, [the DAO layer](https://www.baeldung.com/simplifying-the-data-access-layer-with-spring-and-java-generics) usually consists of a lot of boilerplate code that can and should be simplified. The advantages of such a simplification are many: a decrease in the number of artifacts that we need to define and maintain, consistency of data access patterns, and consistency of configuration.
>
> Spring Data takes this simplification one step further and **makes it possible to remove the DAO implementations entirely**. The interface of the DAO is now the only artifact that we need to explicitly define.
>
> In order to start leveraging the Spring Data programming model with JPA, a DAO interface needs to extend the JPA specific _Repository_ interface, _JpaRepository_. This will enable Spring Data to find this interface and automatically create an implementation for it.

### **Automatic Custom Queries**

When Spring Data creates a new _Repository_ implementation, it analyses all the methods defined by the interfaces and tries to **automatically generate queries from the method names**. While this has some limitations, it’s a very powerful and elegant way of defining new custom access methods with very little effort.
当 Spring Data 创建新的 Repository 实现时,它会分析接口定义的所有方法,并尝试根据方法名称自动生成查询。虽然这有一些限制,但它是一种非常强大且优雅的方式,可以轻松定义新的自定义访问方法。

Let’s look at an example. If the entity has a _name_ field (and the Java Bean standard _getName_ and _setName_ methods), **we’ll define the \*findByName\* method in the DAO interface.** This will automatically generate the correct query:
让我们看一个例子。如果实体有名称字段(以及 Java Bean 标准 getName 和 setName 方法),我们将在 DAO 接口中定义 findByName 方法。这将自动生成正确的查询:

```java
public interface IFooDAO extends JpaRepository<Foo, Long> {

Foo findByName(String name);

}

Manual Custom Queries

Now let’s look at a custom query that we’ll define via the @Query annotation:

@Query("SELECT f FROM Foo f WHERE LOWER(f.name) = LOWER(:name)")
Foo retrieveByName(@Param("name") String name);

Spring Data JPA Repository Configuration

To activate the Spring JPA repository support, we can use the @EnableJpaRepositories annotation and specify the package that contains the DAO interfaces: 要激活 Spring JPA 存储库支持,我们可以使用 @EnableJpaRepositories 注释并指定包含 DAO 接口的包:

@EnableJpaRepositories(basePackages = "com.baeldung.spring.data.persistence.repository")
public class PersistenceConfig {
...
}

Maven 依赖

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.4.0</version>
</dependency>

一对多映射

Simply put, *one-to-many* mapping means that one row in a table is mapped to multiple rows in another table.

For this example, we’ll implement a cart system where we have a table for each cart and another table for each item. One cart can have many items, so here we have a *one-to-many* mapping. 在此示例中,我们将实现一个购物车系统,其中每个购物车都有一个表,每个项目都有另一个表。一辆购物车可以有很多商品,因此这里我们有一个一对多的映射。

The way this works at the database level is we have a cart_id as a primary key in the cart table and also a cart_id as a foreign key in items. 在数据库级别的工作方式是,我们将 cart_id 作为购物车表中的主键,并将 cart_id 作为项目中的外键。

The way we do it in code is with @OneToMany. 我们在代码中执行此操作的方式是使用@OneToMany。

Let’s map the Cart class to the collection of Item objects in a way that reflects the relationship in the database: 让我们以反映数据库中关系的方式将 Cart 类映射到 Item 对象的集合:

public class Cart {

//...

@OneToMany(mappedBy="cart")
private Set<Item> items;

//...
}

We can also add a reference to Cart in each Item using @ManyToOne, making this a bidirectional relationship. Bidirectional means that we are able to access *items* from *carts*, and also *carts* from *items*. 我们还可以使用 @ManyToOne 在每个项目中添加对 Cart 的引用,使其成为双向关系。双向意味着我们能够从购物车访问商品,也可以从商品访问购物车。

The mappedBy property is what we use to tell Hibernate which variable we are using to represent the parent class in our child class. mappedBy 属性是我们用来告诉 Hibernate 我们使用哪个变量来表示子类中的父类的属性。

Hibernate SessionFactory for database intersection

public static SessionFactory getSessionFactory() {

ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(dbSettings())
.build();

Metadata metadata = new MetadataSources(serviceRegistry)
.addAnnotatedClass(Cart.class)
// other domain classes
.buildMetadata();

return metadata.buildSessionFactory();
}

private static Map<String, Object> dbSettings() {
// return Hibernate settings
}

模板制作

connect java and db

OOP object and relational table how to connect?

ORM: object relational db mapping

集成进 IDEA

pom.xml

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.1.2.Final</version>
</dependency>

@Entity 向 hibernate 指明某个 class 需要映射到 db

@Table("tbname") 向 hibernate 指明连接到 tbname 这张表

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

两个连用,指明 id 主键

如果类内的属性和表的列是相同的名字,会自动填充;如果不同,使用@Column(name="...")指定对应的名字

然后要求这个类拥有一个空构造函数(可以有别的辅助构造函数,但要有一个空的重载: 参数和函数体都为空)


https://spring.io/guides/gs/accessing-data-jpa

未注解的属性默认映射到同名列

The other two properties, firstName and lastName, are left unannotated. It is assumed that they are mapped to columns that share the same names as the properties themselves.

Reposity Interface:

通过存储库接口动态地创建 java<->db 映射的 JPA 实现

extends CrudRepository 接口

In a typical Java application, you might expect to write a class that implements CustomerRepository. However, that is what makes Spring Data JPA so powerful: You need not write an implementation of the repository interface. Spring Data JPA creates an implementation when you run the application.

理解示例代码之中最抽象的一段:


@SpringBootApplication
public class AccessingDataJpaApplication {
private static final Logger log = LoggerFactory.getLogger(AccessingDataJpaApplication.class);

public static void main(String[] args) {
SpringApplication.run(AccessingDataJpaApplication.class, args);
}

@Bean
public CommandLineRunner demo(CustomerRepository repo) {
return args -> {
repo.save(new Customer("Jack", "Bauer"));
repo.save(new Customer("Chloe", "O'Brian"));
repo.save(new Customer("Kim", "Bauer"));
repo.save(new Customer("David", "Palmer"));
repo.save(new Customer("Michelle", "Dessler"));

// fetch all customers
log.info("Customers found with findAll():");
log.info("-------------------------------");
repo.findAll().forEach(customer -> {
log.info(customer.toString());
});
log.info("");

// fetch an individual customer by ID
Customer customer = repo.findById(1L);
log.info("Customer found with findById(1L):");
log.info("--------------------------------");
log.info(customer.toString());
log.info("");

// fetch customers by last name
log.info("Customer found with findByLastName('Bauer'):");
log.info("--------------------------------------------");
repo.findByLastName("Bauer").forEach(bauer -> {
log.info(bauer.toString());
});
log.info("");
};
}
}

Q1: 此处的@Bean 是谁来填入的?CustomerRepo 是哪里获取的数据?

A1:

  1. **如果你使用的是嵌入式数据库(如 H2),则不需要提供主机和端口,因为数据库是直接嵌入到应用程序中的。**如果你使用的是外部数据库,你需要确保数据库服务器正在运行,并且网络设置允许你的应用程序连接到数据库服务器,在配置文件之中设置配置。而示例程序是使用 h2 数据库
  2. H2 是一个轻量级的嵌入式数据库,它可以被直接嵌入到 Java 应用程序中。这意味着你不需要安装一个单独的数据库服务器来使用 H2;它可以直接运行在你的 Java 虚拟机(JVM)中。这使得 H2 非常适合用于开发、测试和原型设计,因为它简化了开发环境的设置和管理。通常不需要额外的配置,因为spring-boot-starter-data-jpa启动器已经包含了对 H2 的支持。如果你的application.propertiesapplication.yml文件中没有指定其他的数据库,Spring Boot 将默认使用 H2,并自动创建一个内嵌的 H2 数据库

Q2:CommandLineRunner 和 args->都是啥玩意

A2:

  1. CommandLineRunner 的作用: CommandLineRunner 是 Spring Boot 提供的一个接口,它允许你在应用启动后执行一些代码。实现这个接口的类需要提供一个方法(也就是public static void main(String[] args))run(String... args),这个方法会在 Spring Boot 应用启动完成后执行。CommandLineRunner通常用于执行应用启动后的初始化任务,比如数据加载、发送欢迎邮件、运行一些启动时的检查等。在这个例子中,CommandLineRunner被用来演示如何使用 Spring Data JPA 来操作数据库。

  2. ->是 java 的 lambda 表达式

build executable JAR:

If you use Maven, you can run the application by using ./mvnw spring-boot:run. Alternatively, you can build the JAR file with ./mvnw clean package and then run the JAR file, as follows:

java -jar target/gs-accessing-data-jpa-0.1.0.jar

jar 到底是什么:Java Archive, 是打包 java 类文件的归档文件,基于 zip 格式,可以在任何有 java 运行时的环境下运行

使用 RESTapi 和 JPA

https://spring.io/guides/gs/accessing-data-rest#scratch

关键示例代码

@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long>, CrudRepository<Person,Long> {

List<Person> findByLastName(@Param("name") String name);

}

Q: 第一行的注解是什么意思?

A:

  1. @RepositoryRestResource 是 Spring Data REST 提供的一个注解,用于将 Spring Data 的 Repository 接口暴露为 RESTful 端点。这个注解允许你自定义如何将 Repository 的方法映射到 HTTP 请求上

  2. path = "people"指定了这个 repo 映射到的路由(/people)

  3. collectionResourceRel="people"会自动处理基本的(遵守 JPA 命名约定的)方法映射(替代了@RequestMapping),例如此时发送DELETE /people/:id的请求,即使没有手动实现,也会被默认 mapping 和完成删除

  4. 自定义方法:

    如果你需要执行更复杂的查询,你可以在 Repository 接口中定义自己的方法,并使用@Query注解来提供 JPQL(Java Persistence Query Language)或本地查询(如 JDOQL 或 Hibernate HQL)。例如:

    public interface CustomerRepository extends JpaRepository<Customer, Long> {
    @Query("SELECT c FROM Customer c WHERE c.email = :email")
    Customer findByEmail(String email);
    }
  5. 在运行时,Spring Data REST 自动创建该接口的实现。然后它使用 @RepositoryRestResource 注释来指示 Spring MVC 在 /people 处创建 RESTful 端点。

Loading Comments...