admin 管理员组文章数量: 887007
Gradle+SpringDataJpa+TiDB 实现基本的CRUD操作(基础篇,防踩坑)
学习目标:
Gradle+SpringDataJpa+TiDB 实现基本的CRUD操作(基础篇)
学习内容:
导入依赖:
org.springframework.boot:spring-boot-starter-data-jpa
mysql:mysql-connector-java
dependencies {implementation('org.springframework.boot:spring-boot-starter','org.springframework.boot:spring-boot-starter-data-redis','org.springframework.boot:spring-boot-starter-web','org.springframework.boot:spring-boot-devtools','org.projectlombok:lombok:1.18.18','com.alibaba:fastjson:2.0.15','org.springframework.boot:spring-boot-starter-data-jpa','mysql:mysql-connector-java','org.apache.rocketmq:rocketmq-spring-boot-starter:2.2.1')testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
实体类:
package com.example.tendisgradle.domain;import lombok.Data;import javax.persistence.*;
import java.util.Date;@Data
@Entity(name = "student")
public class Student {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private String id;private String name;private String iphone;private String addr;private Date ctime;private int age;@Transientprivate String other;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getIphone() {return iphone;}public void setIphone(String iphone) {this.iphone = iphone;}public String getAddr() {return addr;}public void setAddr(String addr) {this.addr = addr;}public Date getCtime() {return ctime;}public void setCtime(Date ctime) {this.ctime = ctime;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getOther() {return other;}public void setOther(String other) {this.other = other;}
}
注解@GeneratedValue(strategy = GenerationType.IDENTITY)代表ID自动生成
注解@Transient代表非数据库映射字段
Dao层
@Repository
public interface StudentDao extends JpaRepository<Student,String> {
}
根据业务可以自行改变JpaRepository接口中泛型的值,第一个值为表映射的实体类,第二个值为表主键类型
CRUD
1.插入
private boolean flag = true;@Autowiredprivate StudentDao dao;/*** @description: 新增**/@Scheduled(cron = "0/5 * * * * ?")public void insertData() {if (flag) {flag = false;Student student = new Student();student.setName("麻辣毛蛋");student.setAddr("滨江");student.setCtime(new Date());student.setAge(22);student.setIphone("12345678911");dao.save(student);}}
注意:如果想修改本条数据 save方法慎用! 会把某些没有set的字段给置为null,除非你先查询一遍然后set要修改的数据,但是这样操作,会有大坑等着你跳,详情请看下文修改模块
2.修改
方法一
/*** @description: 修改**/@Scheduled(cron = "0/5 * * * * ?")public void updateData() {if (flag) {flag = false;Optional<Student> op = dao.findById("6341068275337658370");op.ifPresent(student -> {student.setAddr("拱墅");dao.save(student);});}}
写法缺点:与数据库通信两次
方法二
@Scheduled(cron = "0/5 * * * * ?")@Transactionalpublic void testBug() {if (flag) {flag = false;Student student = dao.findById("6341068275337658370").get();student.setAddr("余杭");}}
承接上文所提到的坑,这里原理是利用了持久态的特性,在我们查询数据并对实体类进行set后会直接对数据库进行修改(前提是加上@Transactional注解),这样的缺点是在某些特定的业务条件下我们需要对实体类字段进行修改但是又不想影响到数据库的情况下是很难办的,因此可以注入EntityManager接口调用unwrap方法,使其变成游离态。当然了,在某些不会被影响到的业务中,我们可以利用这种特性来减少与数据库的通信次数。
@Scheduled(cron = "0/5 * * * * ?")@Transactionalpublic void testBug() {if (flag) {flag = false;Session session = entityManager.unwrap(Session.class);Student student = dao.findById("6341068275337658370").get();student.setAddr("余杭");session.evict(student);}}
3.删除
(1)根据Id删除
/*** @description: 删除**/@Scheduled(cron = "0/5 * * * * ?")
// @Transactionalpublic void deleteData() {if (flag) {flag = false;/*Optional<Student> byId = dao.findById("8358680908399640579");Student student = byId.get();*/Student student = new Student();student.setId("6052837899185946634");student.setName("麻辣毛蛋");student.setAge(22);student.setIphone("12345678911");student.setAddr("滨江");student.setCtime(DateUtil.parse("2022-12-12 11:21:40"));dao.delete(student);}}
这里也有个坑,本人亲测过,delete虽说入参是实体类,但是如果实体类中不包含ID字段的话,就无法删除,根据我的个人理解,也就是说,本质上我可以把它当成deleteById,那我就不理解jpa给一个这个delete方法的意义在哪?我直接deleteById删除不就好咯?
(2)Dao层自定义删除
@Repository
public interface StudentDao extends JpaRepository<Student,String> {void deleteByNameAndAge(String name ,int age);}
/*** @description: 删除**/@Scheduled(cron = "0/5 * * * * ?")@Transactionalpublic void deleteData() {if (flag) {flag = false;dao.deleteByNameAndAge("麻辣毛蛋",22);}}
注意:dao层自定义CRUD时,删除方法较为特殊,需要在业务层中加上@Transactional方法才会删掉,不然会报如下错误:
org.springframework.dao.InvalidDataAccessApiUsageException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call; nested exception is javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' callat org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:403) ~[spring-orm-5.3.23.jar:5.3.23]at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:235) ~[spring-orm-5.3.23.jar:5.3.23]at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551) ~[spring-orm-5.3.23.jar:5.3.23]at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) ~[spring-tx-5.3.23.jar:5.3.23]at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242) ~[spring-tx-5.3.23.jar:5.3.23]at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152) ~[spring-tx-5.3.23.jar:5.3.23]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.23.jar:5.3.23]at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:145) ~[spring-data-jpa-2.7.5.jar:2.7.5]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.23.jar:5.3.23]at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.23.jar:5.3.23]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.23.jar:5.3.23]at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.23.jar:5.3.23]at jdk.proxy3/jdk.proxy3.$Proxy111.deleteByNameAndAge(Unknown Source) ~[na:na]at com.example.tendisgradle.core.scheduling.JpaTest.deleteData(JpaTest.java:86) ~[main/:na]at com.example.tendisgradle.core.scheduling.JpaTest$$FastClassBySpringCGLIB$$d303fb7d.invoke(<generated>) ~[main/:na]at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.23.jar:5.3.23]at org.springframework.aop.framework.CglibAopProxy.invokeMethod(CglibAopProxy.java:386) ~[spring-aop-5.3.23.jar:5.3.23]at org.springframework.aop.framework.CglibAopProxy.access$000(CglibAopProxy.java:85) ~[spring-aop-5.3.23.jar:5.3.23]at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:704) ~[spring-aop-5.3.23.jar:5.3.23]at com.example.tendisgradle.core.scheduling.JpaTest$$EnhancerBySpringCGLIB$$79037395.deleteData(<generated>) ~[main/:na]at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) ~[spring-context-5.3.23.jar:5.3.23]at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-5.3.23.jar:5.3.23]at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:95) ~[spring-context-5.3.23.jar:5.3.23]at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) ~[na:na]at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Caused by: javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' callat org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:295) ~[spring-orm-5.3.23.jar:5.3.23]at jdk.proxy3/jdk.proxy3.$Proxy101.remove(Unknown Source) ~[na:na]at org.springframework.data.jpa.repository.query.JpaQueryExecution$DeleteExecution.doExecute(JpaQueryExecution.java:277) ~[spring-data-jpa-2.7.5.jar:2.7.5]at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:90) ~[spring-data-jpa-2.7.5.jar:2.7.5]at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:156) ~[spring-data-jpa-2.7.5.jar:2.7.5]at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:144) ~[spring-data-jpa-2.7.5.jar:2.7.5]at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137) ~[spring-data-commons-2.7.5.jar:2.7.5]at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121) ~[spring-data-commons-2.7.5.jar:2.7.5]at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:160) ~[spring-data-commons-2.7.5.jar:2.7.5]at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:139) ~[spring-data-commons-2.7.5.jar:2.7.5]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.23.jar:5.3.23]at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:81) ~[spring-data-commons-2.7.5.jar:2.7.5]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.23.jar:5.3.23]at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.23.jar:5.3.23]at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.23.jar:5.3.23]at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.23.jar:5.3.23]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.23.jar:5.3.23]at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-5.3.23.jar:5.3.23]... 27 common frames omitted
4.查询
@Scheduled(cron = "0/5 * * * * ?")public void queryData(){Optional<Student> byId = dao.findById("6052837899185946636");Student student = dao.findByName("麻辣毛蛋");}
综上,是小郭对JPA的初体验,下篇文章,我将对项目当中常用到的复杂或者进阶的CRUD展开讨论,例如动态模糊查询,分页,分组聚合等。
如有表述不当或理解错误请联系邮箱:947476994@qq
本文标签: GradleSpringDataJpaTiDB 实现基本的CRUD操作(基础篇,防踩坑)
版权声明:本文标题:Gradle+SpringDataJpa+TiDB 实现基本的CRUD操作(基础篇,防踩坑) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1732354145h1533948.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论