【我的学习系列之Spring Data JPA】二、Spring Data JPA之CRUD

Spring Data JPA之CRUD

事务管理

     在数据库操作中添加数据的时候,是需要使用到事务来进行管理的,而在上面的添加User数据的示例中,并没有看到在哪里使用了事务管理,那么它是怎么起作用的呢???
    需要知道的是,如果没有事务,我们对数据表的增删改操作不可能会成功的。那么问题就来了,SpringData 是在哪里进行了事务的管理呢??
    在示例中,通过userDao对象调用了save()方法,这个方法是由CrudRepository接口提供的,而接口,只定义了一个方法主体,并没有具体的实现,那么问题又来了,它是怎么实体数据的保存的???
    这就要来看SimpleJpaRepository类了:

@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {}

public interface JpaRepositoryImplementation<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {}

public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {}

public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID>{}

    从上表可以看到,SimpleJpaRepository是CrudRepository接口的间接实现类,而在SimpleJpaRepository类上,使用了@Transactional(readOnly = true)注解,在类上使用@Transactional注解,表示这个类中所有的方法都开启事务管理,readOnly = true的意思是,所有的事务,都是只读模式的,只读,意味着增删改无法正常使用到事务。
    再来看save()方法的源码:
@Transactional
public <S extends T> S save(S entity) {
......
}

    在save()方法方法上,还是使用了@Transactional,这表示当前的方法正常开启事务的应用,有了这个注解,在类上标注的事务功能就对save()方法没有效果了,此时就不再是只读了,而是可以正常使用的增删改事务管理。
    是的,在Spring中,事务的管理,可以使用注解@Transactional来实现。     而对于事务,@Transactional这个注解只会在RuntimeException(运行时异常),这种异常时才会进行数据回滚,而Exception(受检异常)抛出的时候,是不会进行数据回滚的,这个时候我们想他报Exception异常的时候依旧想他进行数据回滚要怎么办。只需要在@Transactional后面加上(rollbackFor = Exception.class),就行了@Transactional(rollbackFor = Exception.class)。

    同样的,删除delete()方法也是使用了这个注解。
    同一个方法中多个数据库操作,使用注解之后,就变成一笔业务,由于事务的原子性和一致性,要么都成功,要么就一起不成功!!

删除数据

接下来测试一下,使用SpringData JPA如何删除数据。在测试类中添加一个删除数据的方法:

 @Test
    void deleteUserTest() {
        userDao.deleteById(3);
    }

测试结果: 删除uid=3的User数据的操作!
Hibernate: select user0_.uid as uid1_0_0_, user0_.birthday as birthday2_0_0_, user0_.password as password3_0_0_, user0_.u_sex as u_sex4_0_0_, user0_.username as username5_0_0_ from t_user user0_ where user0_.uid=?
Hibernate: delete from t_user where uid=?

    这两条SQL语句,是由Hibernate框架自动生成的,在查询数据的时候,会自动使用as关键字来进行别名的设置。 测试是成功了,从控制台给出的SQL操作语句来看,先进行了查询是否存在指定的主键,如果有就进行删除。如果没有,就会报异常:
org.springframework.dao.EmptyResultDataAccessException: No class com.edu118.entity.User entity with id 3 exists!

修改数据

新增和删除都测试过后,现在来看一下,修改用户数据要如何来实现。
在SpringData的应用中,如果要修改数据,使用的是save()方法,很奇怪吧。看源码就知道了:

在这里插入图片描述
    在save()方法中,通过isNew()方法来判断要保存的实体类是否已经存在,,如果不存在就使用persist()保存,如果存在了并且有主键值,就使用merge()方法进行修改保存。
知道了方式,现在就来进行数据的修改吧,在测试类中定义一个修改的方法:
在这里插入图片描述


测试之后,发现是添加新的用户数据。修改密码:

在这里插入图片描述
如果数据设置上了id,也就是主键值,那么,会先进行主键的查询,如果有数据,那么就做修改保存。
修改数据的时候,是所有的属性都参与修改操作。

查询单条数据


增删改都完成之后,就是数据的查询了,先来以主键进行查询。 测试方法:

在这里插入图片描述

在这里插入图片描述
    从代码中可以看到,要查询指定主键值的数据,可以使用findById()方法来完成,传入要查询的主键值就可以了,但是,查询的结果却是返回Optional对象。
    Optional 类主要解决的问题是臭名昭著的空指针异常,从 Java 8 引入的一个很有趣的特性是 Optional 类。本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。


    另外,如果是查询单条数据,还可以使用另外两个方法:

findOne(Example<S> example)

根据条件查询单条数据,在早期版本,是直接通过主键查询的,在新版本中,更新了功能,可以自定义查询的条件。Example就是查询条件的封装类,通过它的静态方法of(),来接收一个实体类对象,根据这个对象非null的属性来确定查询的条件。
//以主键查询数据

@Test
void findUserById2() {
    User user = new User();
    user.setUid(2);
    user.setUsername("123");
    Optional<User> optionalUser = userDao.findOne(Example.of(user));
}

    在上面的示例中,给Example绑定了一个User对象,这个User对象中,设置了uid和username两个属性,意思是查询uid和username相匹配的数据,查询的条件是uid=? and username = ? ,同时满足给出的两个条件;

第二种是getOne方法

getOne(ID id)

    使用这个方法,有延迟加载的功能ID是类上声明的泛型,实体类中主键的数据类型是什么,这个ID就是什么。
    如果return null,要执行请求的时候,是不会进行数据库的查询的,这就是延迟加载,也叫做懒加载模式,当真正使用到数据的时候,才会去进行数据的查询,比如return user时,把查询到的数据返回给页面,这就使用到了查询的结果,因此才会真正的进行数据的查询操作。
    在使用getOne()的时候需要注意一点,如果要操作中出现了这样的异常:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
 No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor 
 and no properties discovered to create BeanSerializer (to avoid exception, disable 
 SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.nnxy.entity.User$HibernateProxy$JmI5SdQM["hibernateLazyInitializer"])

    出现这个异常,是因为SpringBoot中使用Hibernate的延迟加载功能,Hibernate每次都自动给操作的实体类添加一个hibernateLazyInitializer的临时属性。
    当我们从查询中去获取加载的数据转换成JSON数据并返回时,在User实体类中找不到hibernateLazyInitializer这个临时属性,所以就报异常了。
    如果返回JSON数据,在SpringMVC中默认使用的JSON转换工具,是jackson,因此报的是jackson相关包下的异常类InvalidDefinitionException,无效的定义异常。
    也就是说,实际上,出现错误是在数据转换的这个环节,查询到的数据中多了一个属性,而实体类中并没有这个属性,所以,转换失败。
    解决的方式也很简单,既然查询的数据中多了一个属性,那么在转换成JSON数据的时候,不使用这个属性就可以了,而jackson工具,提供了一个注解,在数据转换的时候,可以忽略掉指定的属性:

在这里插入图片描述

    只要把指定的属性去掉,转换JSON的时候不使用它就可以了,@JsonIgnoreProperties就是用来干这个事情的,可以同时忽略多个属性,因为它的value是一个数组。
    而@JsonIgnoreProperties注解,可以贴在类上,指定哪些属性不使用,也可以直接贴在属性上,这样就表示当前的属性被忽略,不参与JSON的转换。
    在User类中,并没有hibernateLazyInitializer这个属性,所以就只能在类上贴,并指定把它忽略掉。
    至此,查询单条数据的3种方式,都已经给大家作了说明,请大家好好测试!!

统计数据

   &nbsp要知道数据表中有多少条数据,就需要进行统计查询,而要统计查询,也非常的简单,看代码:

在这里插入图片描述


至此,Spring Data JPA之CRUD的基本功能测试以及完结,下一章我们来真正的了解Spring Data JPA的高级用法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值