通过Spring Data JPA操作数据库(以H2为例)

通过Spring Data JPA操作H2数据库

详细代码见GitHub仓库~


常见JPA注解

定义实体:

  1. @Entity
  2. @MappedSuperclass:定义多个实体类的父类
  3. @Table(name)

主键:

  1. @Id:注解

  2. @GeneratedValue(strategy, generator):自增

    strategy = GenerationType.IDENTITY:自增主键

  3. @SequenceGenerator(name, sequenceName):序列

映射:

  1. @Column(name, nullable, length, insertable, updatable)

    update = false:意味着该字段创建后不允许被修改

  2. @JoinTable(name):通过中间表关联

  3. @JoinColumn(name)

  4. @Enumerated映射枚举值

关系:

  1. @OneToOne
  2. @OneToMany
  3. @ManyToOne
  4. @ManyToMany:多对多关系
  5. @OrderBy

常见Lombok注解:
@Data:自动创建getter&setter&toString
@Getter:自动创建getter
@Setter:自动创建setter
@ToString(callSuper):自动创建toString(),callSuper = true时toString会把父类字段也算上
@NoArgsConstructor:无参构造函数
@AllArgsConstructor:全参构造函数
@Builder创建构造器函数


操作步骤

1.导入 Spring Data JPA & H2内存数据库 依赖

pom.xml文件中添加如下依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>

<dependencies>
<!-- JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- H2内存数据库 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>

<!--以下是非必须依赖:-->

<!-- 高精度货币:joda-money -->
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-money</artifactId>
<version>1.0.1</version>
</dependency>
<!-- 金额转换:jadira.usertype-->
<dependency>
<groupId>org.jadira.usertype</groupId>
<artifactId>usertype.core</artifactId>
<version>6.0.1.GA</version>
</dependency>

<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

2.修改application.properties文件

1
2
3
4
5
# Hibernate(JPA)相关
#创建后删除
spring.jpa.hibernate.ddl-auto=create-drop
#打印SQL
spring.jpa.show-sql=true

3.创建实体类

  1. 创建实体类BaseEntity,通过@MappedSuperclass标记为其他实体类的父类,主要包含主键、创建时间、更新时间3个字段。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@MappedSuperclass
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BaseEntity implements Serializable {
//自增主键
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//创建时间不允许修改
@Column(updatable = false)
@CreationTimestamp
private Date createdTime;
@UpdateTimestamp
private Date updatedTime;
}
  1. 创建实体类Coffee,通过@Entity进行标注,继承BaseEntity,和T_MENU表进行映射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Data
//toString时加上父类字段
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
//声明实体
@Entity
//和DB表关联
@Table(name = "T_MENU")
public class Coffee extends BaseEntity implements Serializable {
private String name;
//joda-money的Money类型,通过jadira.usertype和人民币的分进行映射,100.34存储为10034
@Type(type = "org.jadira.usertype.moneyandcurrency.joda.PersistentMoneyMinorAmount",
parameters = {@org.hibernate.annotations.Parameter(name = "currencyCode", value = "CNY")})
private Money price;
}
  1. 创建实体类Order,通过@Entity进行标注,继承BaseEntity,和T_ORDER表进行映射,和T_MENU通过中间表T_ORDER_COFFEE进行关联
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Data
//toString时加上父类字段
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
//声明实体
@Entity
//和DB表关联
@Table(name = "T_ORDER")
public class Order extends BaseEntity implements Serializable {
private String customer;
//Coffe(menu)和Order是多对多
@ManyToMany
//通过中间表t_order_coffee进行连接
@JoinTable(name = "T_ORDER_COFFEE")
private List<Coffee> items;
//订单状态,枚举值
@Enumerated
private OrderState state;
}
// public enum OrderState {
// INIT,
// PAID,
// BREWING,
// BREWED,
// TAKEN,
// CANCELLED
// }

4.操作数据库

(1)Repository

  1. 创建接口继承JpaRepository<T, ID>/CrudRepositor<T, ID>/PagingAndSortingRepository<T, ID>

    如果需要用到分页必须继承PagingAndSortingRepository<T, ID>,其中ID参数是标注了@Entity的实体类的主键类型,比如Long

1
2
public interface CoffeeRepository extends JpaRepository<Coffee, Long> {...}
public interface OrderRepository extends JpaRepository<Order, Long> {...}
  1. 在启动类上添加@EnableJpaRepositories
1
2
3
@SpringBootApplication
@EnableJpaRepositories
public class JPAManipulateH2Application {...}

(2)查询

  1. 根据方法名定义查询:
  • find...By.../read...By.../query...By.../get...By...:查找
  • count...By...:计数
  • ...OrderBy...[Asc/Desc]:升序/降序
  • And/Or/IgnoreCase:and/or/忽略大小写
  • Top/First/Distinct
  1. 分页查询(必须继承PagingAndSortingRepository):
  • Pageable/Sort
  • Slice<T>/Page<T>

比如:

1
2
3
4
5
public interface OrderRepository extends JpaRepository<Order, Long> {
//findBy...OrderBy...
//通过顾客名称查找,按ID排序
List<Order> findByCustomerOrderById(String customer);
}

(3)保存

调用Repository的save(Entity e)方法。

5.主程序入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@Slf4j
@SpringBootApplication
//JPA
@EnableJpaRepositories
//事务管理器
@EnableTransactionManagement
//继承ApplicationRunner,在run()中定义运行时要执行的方法
public class JPAManipulateH2Application implements ApplicationRunner {

@Autowired
private CoffeeRepository coffeeRepository;

@Autowired
private OrderRepository orderRepository;

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

@Override
@Transactional
//以事务的模式运行,保证操作具有原子性
public void run(ApplicationArguments args) throws Exception {
initOrders();
findOrders();
}

//生成订单
private void initOrders() {
Coffee latte = Coffee.builder().name("latte")
.price(Money.of(CurrencyUnit.of("CNY"), 30.0) )
.build(); //拿铁
coffeeRepository.save(latte); //保存
log.info("Coffee: {}", latte);
Order order = Order.builder().customer("DragonBaby")
.items(Collections.singletonList(latte))
.state(OrderState.INIT)
.build();
orderRepository.save(order);
log.info("Order: {}", order);
}

//查找前3个订单
private void findOrders() {
List<Order> orders = orderRepository.findByCustomerOrderById("DragonBaby");
orders.forEach(order -> log.info("DragonBaby的订单:{}", order));
}
}

运行可以看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Hibernate: drop table t_menu if exists
Hibernate: drop table t_order if exists
Hibernate: drop table t_order_coffee if exists
Hibernate: create table t_menu (id bigint generated by default as identity, created_time timestamp, updated_time timestamp, name varchar(255), price bigint, primary key (id))
Hibernate: create table t_order (id bigint generated by default as identity, created_time timestamp, updated_time timestamp, customer varchar(255), state integer, primary key (id))

# 创建中间表,设置外键
Hibernate: create table t_order_coffee (order_id bigint not null, items_id bigint not null)
Hibernate: alter table t_order_coffee add constraint FKj2swxd3y69u2tfvalju7sr07q foreign key (items_id) references t_menu
Hibernate: alter table t_order_coffee add constraint FKjnvwi9aq5s71k7dru924mleq7 foreign key (order_id) references t_order

Hibernate: insert into t_menu (id, created_time, updated_time, name, price) values (null, ?, ?, ?, ?)
2019-08-04 00:32:02.947 INFO 13788 --- [ main] com.bat.JPAManipulateH2Application : Coffee: Coffee(super=BaseEntity(id=1, createdTime=Sun Aug 04 00:32:02 CST 2019, updatedTime=Sun Aug 04 00:32:02 CST 2019), name=latte, price=CNY 30.00)
Hibernate: insert into t_order (id, created_time, updated_time, customer, state) values (null, ?, ?, ?, ?)
2019-08-04 00:32:02.961 INFO 13788 --- [ main] com.bat.JPAManipulateH2Application : Order: Order(super=BaseEntity(id=1, createdTime=Sun Aug 04 00:32:02 CST 2019, updatedTime=Sun Aug 04 00:32:02 CST 2019), customer=DragonBaby, items=[Coffee(super=BaseEntity(id=1, createdTime=Sun Aug 04 00:32:02 CST 2019, updatedTime=Sun Aug 04 00:32:02 CST 2019), name=latte, price=CNY 30.00)], state=INIT)

Hibernate: select order0_.id as id1_1_, order0_.created_time as created_2_1_, order0_.updated_time as updated_3_1_, order0_.customer as customer4_1_, order0_.state as state5_1_ from t_order order0_ where order0_.customer=? order by order0_.id asc
2019-08-04 00:32:03.162 INFO 13788 --- [ main] com.bat.JPAManipulateH2Application : DragonBaby的订单:Order(super=BaseEntity(id=1, createdTime=Sun Aug 04 00:32:02 CST 2019, updatedTime=Sun Aug 04 00:32:02 CST 2019), customer=DragonBaby, items=[Coffee(super=BaseEntity(id=1, createdTime=Sun Aug 04 00:32:02 CST 2019, updatedTime=Sun Aug 04 00:32:02 CST 2019), name=latte, price=CNY 30.00)], state=INIT)
Hibernate: insert into t_order_coffee (order_id, items_id) values (?, ?)

Hibernate: drop table t_menu if exists
Hibernate: drop table t_order if exists
Hibernate: drop table t_order_coffee if exists

原理

Repository接口是如何变为可使用的Bean的呢?我们在接口中定义了方法却没有去实现,Spring是如何帮我做对应的操作呢?

  1. JpaRepositoryRegistrar激活了@EnableJpaRepositories注解,返回JpaRepositoryConfigExtension
  2. RepositoryBeanDefinitionRegistrarSupport.registerBeanDefinitions()为每一个Repository注册一个JpaRepositoryFactoryBean
  3. RepositoryConfigurationExtensionSupport.getRepositoryConfigurations()获取所有Repository的配置,方便创建Bean
  4. JpaRepositoryFactory.getTargetRepository()创建Repository
-------------本文结束感谢您的阅读-------------

本文标题:通过Spring Data JPA操作数据库(以H2为例)

文章作者:DragonBaby308

发布时间:2019年08月03日 - 20:22

最后更新:2019年09月22日 - 10:13

原始链接:http://www.dragonbaby308.com/JPA/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

急事可以使用右下角的DaoVoice,我绑定了微信会立即回复,否则还是推荐Valine留言喔( ఠൠఠ )ノ
0%