spring data jpa 入门看这一篇文章就够了
目录
项目构建
- 新建项目,引入maven坐标
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
</dependency>
</dependencies>
- 数据库表sql
CREATE TABLE `goods` (
`id` int(0) NOT NULL AUTO_INCREMENT COMMENT '主键',
`good_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商品名称',
`good_price` decimal(10, 2) NOT NULL COMMENT '商品单价',
`add_time` timestamp(0) NOT NULL COMMENT '添加时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
SET FOREIGN_KEY_CHECKS = 1;
- 搭建springboot项目,由于项目比较简单,就不再演示.展示目录结构。抛开所有的杂七杂八的,我们只看我们想要的,直接明了。 但是需要在我们的启动类,也就是DemoApplication上增加扫描注解
@ComponentScan(basePackages = {"com.zhw.jpademo.demo.dao"}) 扫描dao包下所有的Repository
-
- 配置项目application.yml
server:
port: 8081
tomcat:
max-http-header-size: 8192
spring:
#mysql config 配置数据库连接
datasource:
username: root
password: root
url: jdbc:mysql://127.0.0.1:3306/shop_goods
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
jpa:
#在控制台打印sql
show-sql: true
hibernate:
#ddl-auto:create----每次运行该程序,没有表格会新建表格,表内有数据会清空
#ddl-auto:create-drop----每次程序结束的时候会清空表
#ddl-auto:update----每次运行程序,没有表格会新建表格,表内有数据不会清空,只会更新
#ddl-auto:validate----运行程序会校验数据与数据库的字段类型是否相同,不同会报错
ddl-auto: update
快速入门实操
- Goods实体类,这个实体类很关键,@Table注解用来标识实体类与数据表的对应关系,@Column注解来标识实体类中属性与数据表中字段的对应关系
@Data
@Entity
@Table(name = "goods")
public class Goods {
@Id //主键id
@GeneratedValue(strategy= GenerationType.IDENTITY)//主键生成策略
@Column(name="id")//数据库字段名
private Integer id;
@Column(name = "good_name")
private String goodName;
@Column(name = "good_price")
private BigDecimal goodPrice;
@Column(name = "add_time")
private Timestamp addTime;
}
- 定义完实体类后,我们开始编写我们的持久层,也就是 GoodsRepository,使用jpa很简单,只需要继承一下 JpaRepository<T, ID>。不需要在添加任何注解。
- 其中T表示要 实体类名,ID表示主键类型。代码如下
public interface GoodsRepository extends JpaRepository<Goods,Integer> { }
-
接下来我们直接在Controller中操作一下试试手。
@RestController
@SpringBootApplication
@ComponentScan(basePackages = {"com.zhw.jpademo.demo.dao"})
public class DemoApplication {
// 我们注入GoodsRepository
@Autowired
private GoodsRepository goodsRepository;
@RequestMapping(value = "/")
public String home() {
// findAll 查找表下所有的数据,具体代码可以查看JpaRepository接口中提供的方法
List<Goods> all = goodsRepository.findAll();
System.out.println(all);
}
扩展使用
- 当然除了JpaRepository中提供的方法,我们还可以有额外的使用,例如当我们查询名称为 商品3的数据时,我们可以这样写。findByGoodName。只要按照驼峰命名,jpa关键字去编写,jpa就能识别成功!
Goods goods1 = goodsRepository.findByGoodName("商品3");
System.out.println(goods1);
- 那么有些同学要问了,如果我想使用sql,该怎么操作呢?看 扩展使用二 把。
扩展使用二:自定义sql @Query
注意注意:注解中写的是HQL,所以查询的是对象,而不是表名,我们的对象是Goods 而不是数据库中的goods表,查询的字段也是对象的成员,不是表的字段。(好多文章没有提及这个,导致很多程序运行报错)
/**
* 自定义sql查询脚本
* 注解中写的是HQL,所以查询的是对象,而不是表名
* 同样的问题,查询的字段也是对象的成员,不是表的字段
* @param name
* @return
*/
@Query(" SELECT g FROM Goods g WHERE goodName LIKE concat('%', :name, '%') ")
List<Goods> myGoods(@Param("name") String name);
- 我们先在用2种方法增加一条数据。值得一提的是
-
Spring Data 提供 了默认的事务处理方式,即 所有的查询均声明为只读 事务 。
-
对于自定义的方法,如需 改变 S pringData 提供 的事务默认方式,可以在方法上 注解 @Transactional 声明
-
在Controller,或者Service 上使用 @Transactional。我的demo中就直接在Controller上加了
-
- 第一种,使用JpaRepository中的save方法。有趣的是,save方法不仅是新增,还是更新,当我表中的数据(主键)存在时,便会更新这条数据。可以试试哦。测试代码如下:
// 因为主键策略是自动新增,从1开始,那么这段代码执行成功后,数据库中会多出一条id=1的数据 Goods goods = new Goods(); goods.setAddTime(new Timestamp(System.currentTimeMillis())); goods.setGoodName("商品2"); goods.setGoodPrice(new BigDecimal(0.01)); goodsRepository.save(goods); // 然后我们使用save去更新这条数据,设置id为1 Goods goods2 = new Goods(); goods2.setId(1); goods2.setAddTime(new Timestamp(System.currentTimeMillis())); goods2.setGoodName("商品3"); goods.setGoodPrice(new BigDecimal(0.01)); goodsRepository.save(goods2);
- 第二种,使用@query方法去保存,我们编写 GoodsRepository
- 在@Query注解中编写JPQL实现 DELETE 和 UPDATE 操作的时候必须加上@modifying注解
/**
* 自定义sql 插入脚本
* @param goods
* @return
*/
@Modifying
@Query(value = " INSERT INTO goods (good_name, good_price, add_time) VALUES (:#{#igoods.goodName},:#{#igoods.goodPrice},:#{#igoods.addTime} ) ",nativeQuery = true)
int addNewGoods(@Param("igoods") Goods goods);
- 发现什么不一样了吗?我们在 【查询】操作的时,查询的是对象不是表名。但是在【插入】操作时候则直接写的是insert into goods 是数据库的表,而不是Goods对象。属性也是同样的到了.
- 这一切都是 【nativeQuery】的作用,当为 true 时是可以执行原生sql语句,所谓原生sql,也就是说这段sql拷贝到数据库中。
- 就不是原生sql,而其中的 select * from xxx中xxx也不是数据库对应的真正的表名,而是对应的实体名,并且sql中的字段名也不是数据库中真正的字段名,而是实体的字段名
-
值的一提的是,如果你们执行代码抛出异常如下。则说明你们在更新操作时候没有加 @Transactional .
javax.persistence.TransactionRequiredException: Executing an update/delete query at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:413) ~[hibernate-core-5.4.27.Final.jar:5.4.27.Final] at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1668) ~[hibernate-core-5.4.27.Final.jar:5.4.27.Final] at org.springframework.data.jpa.repository.query.JpaQueryExecution$ModifyingExecution.doExecute(JpaQueryExecution.java:239) ~[spring-data-jpa-2.4.3.jar:2.4.3] at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:88) ~[spring-data-jpa-2.4.3.jar:2.4.3] at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:155) ~[spring-data-jpa-2.4.3.jar:2.4.3]
emmm,目前就写完了,哈哈哈哈。所有的源码如下
Controller:@RestController @SpringBootApplication @ComponentScan(basePackages = {"com.zhw.jpademo.demo.dao"}) public class DemoApplication { @Autowired private GoodsRepository goodsRepository; @RequestMapping(value = "/") @Transactional public String home() { // List<Goods> all = goodsRepository.findAll(); // System.out.println(all); // // Goods goods = new Goods(); // goods.setAddTime(new Timestamp(System.currentTimeMillis())); // goods.setGoodName("商品2"); // goods.setGoodPrice("0.01"); // goodsRepository.save(goods); // // Goods goods2 = new Goods(); // goods2.setId(5); // goods2.setAddTime(new Timestamp(System.currentTimeMillis())); // goods2.setGoodName("商品3"); // goods2.setGoodPrice("0.03"); // goodsRepository.save(goods2); //Goods goods1 = goodsRepository.findByGoodName("商品3"); //System.out.println(goods1); List<Goods> allGoods = goodsRepository.findAllByGoodName("商品2"); System.out.println(allGoods); List<Goods> findGoods = goodsRepository.myGoods("3"); System.out.println(findGoods); // goods1.setGoodName("新商品"); // int update = goodsRepository.updateGoods(goods1); // System.out.println(update); Goods newGoods = new Goods(); newGoods.setGoodName("新增商品"); newGoods.setAddTime(new Timestamp(System.currentTimeMillis())); newGoods.setGoodPrice(new BigDecimal(20.12)); goodsRepository.addNewGoods(newGoods); return "服务启动成功"; } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
GoodsRepository
/** * @author ZhangHaiWen * @title: GoodsRepository * @projectName minipasscheck * @description: 商户response * @date 2021/2/1415:44 */ public interface GoodsRepository extends JpaRepository<Goods,Integer> { @Override List<Goods> findAll(); /** * 根据商品名称查询商品 * @param name * @return */ Goods findByGoodName(String name); /** * 根据商品名称查询所有商品 * @param name * @return */ List<Goods> findAllByGoodName(String name); /** * 自定义sql查询脚本 * 注解中写的是HQL,所以查询的是对象,而不是表名 * 同样的问题,查询的字段也是对象的成员,不是表的字段 * @param name * @return */ @Query(" SELECT g FROM Goods g WHERE goodName LIKE concat('%', :name, '%') ") List<Goods> myGoods(@Param("name") String name); /** * 自定义sql 插入脚本 * @param goods * @return */ @Modifying @Query(value = " INSERT INTO goods (good_name, good_price, add_time) VALUES (:#{#igoods.goodName},:#{#igoods.goodPrice},:#{#igoods.addTime} ) ",nativeQuery = true) int addNewGoods(@Param("igoods") Goods goods); /** * 自定义sql 更新操作 * @param goods * @return */ @Modifying @Query(value = "update goods set good_name = :#{#igoods.goodName} where id = :#{#igoods.id}",nativeQuery = true) int updateGoods(@Param("igoods") Goods goods); /** * 自定义sql 更新操作 * @param goods * @return */ @Modifying @Query(value = "update Goods set goodName = :#{#igoods.goodName} where id = :#{#igoods.id}") int updateGoodsNonativeQuery(@Param("igoods") Goods goods); }