基于SpringBoot打造在线教育系统(2)-- 深刻学习JPA与Junit测试

1.我要添加一条用户数据

如今User表已经有了,并且对应mysql数据库里面,已经建好了用户表。

我琢磨着不是要作登陆功能嘛,那就得先往User表里头添加一条数据啊。用mysql front直接添加确定是能够的,不过前段时间正好看了兔子发在B站的SSM商城系统,里面好像有个地方可以直接用Junit Test测试的,虽然这个系统不是SSM,不过应该也能够吧。java

对了,pom.xml里面不是有这么一段配置嘛:mysql

<!-- springboot test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

这玩意怎么看都像是测试用的依赖啊,这是否是意味着,我就不用跟视频里面那样,去引入jar包了??web

嗯,确定是的。spring

2. 作单元测试插入数据

OK,说干就干,建立一个测试包和测试类:
sql

package com.edu.test;

import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;

@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class UserTest {

}

打上注解,额,对了,我TM好像还没写dao方法呢,赶忙去写个。

建立一个dao包,这个包里面都放持久层的类,如今添加一个UserDao的接口。兔子关于SpringBoot的文章里面已经写过怎么使用JPA了,这边依葫芦画瓢。

直接写一个UserDao接口,继承一下JPA,注意,包别导错了。数据库

package com.edu.dao; 

import org.springframework.data.jpa.repository.JpaRepository;
import com.edu.entity.User;

public interface UserDao  extends JpaRepository<User, String>{

}

这样就ok了,而后,回到测试类,把这个接口注入进去。
springboot

add测试方法框架

@Test
	public void addUser(){
		User user = new User();
		user.setUserName("root");
		user.setPassword("root");
		user.setCreateTime("20210103");
		user.setNickName("剽悍一小兔");
		user.setRoleId("1");//默认1是管理员
		user.setIsDelete("0");//默认不删除
		user.setIsLogined("0");//默认没有登陆
		
		userDao.save(user);
		
		System.out.println("保存成功!");
		
	}

开始测试:


spring-boot

哇,真的好了嘛,赶忙看下数据库??单元测试

OK了,真的来了。

3. 单元测试优化

我叶小凡居然也能够触类旁通啦,兔子还没出这个SpringBoot版本的测试教程呢,我就凭借本身惊人的天赋,提早搞定了,哈哈哈。

兔子:“你这个单元测试还能够优化哦,亲~”

“啥状况,这不是很完美嘛?”

兔子:“这只是第一个单元测试,后面可能还会有不少其余的单元测试,你能够作一个通用的父类,这样就不用在每一个测试类上打那么多注解了。”

因而,在兔子的指导下,我虽然不服,但仍是照作了。

这样,在测试包下面,咱们建立一个通用的测试父类。

package com.edu.test;

import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;

@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class BaseTest {
	
	@Before
    public void init() {
        System.out.println("开始测试-----------------");
    }
 
    @After
    public void after() {
        System.out.println("测试结束-----------------");
    }

}

而后,UserTest就继承这个父类,不用再加测试的注解了。

public class UserTest extends BaseTest{

}

验证一下,咱们再写个测试方法,把刚才的数据删掉。

使用jpa进行update操做主要有两种方式:

一、调用保存实体的方法

1)保存一个实体:repository.save(T entity)

2)保存多个实体:repository.save(Iterable entities)

3)保存并当即刷新一个实体:repository.saveAndFlush(T entity)

注:如果更改,entity中必须设置了主键字段,否则不能对应上数据库中的记录,变成新增(数据库自动生成主键)或报错(数据库不自动生成主键)了

二、@Query注解,本身写JPQL语句

   @Modifying
   @Query("update ShopCoupon sc set sc.deleted = true where sc.id in :ids")
   public void deleteByIds(@Param(value = "ids") List<String> ids);

1)update或delete时必须使用@Modifying对方法进行注解,才能使得ORM知道如今要执行的是写操做

2)有时候不加@Param注解参数,可能会报以下异常:

org.springframework.dao.InvalidDataAccessApiUsageException: Name must not be null or empty!; nested exception i                is Java.lang.IllegalArgumentException: Name must not be null or empty!

以上资料摘自百度,哈哈,我该用哪种呢?第二种方法比较亲切,直接用sql语句了,那就使用第二种吧。

按照百度到的说法,先在dao增长一个方法,本身写jpql语句,其实我也不太懂啥叫jpql语句,估计意思就是正常写sql,可是呢,字段的名字和User类里面的字段保持一致就好了。由于我发现,生成的表,仍是用了下划线,是这样的:

因而,我就不能用下划线。

public interface UserDao  extends JpaRepository<User, String>{

	@Modifying
    @Query("update User u set u.isDelete = 1 where u.userName = :userName")
    public void deleteByUsername(@Param(value = "userName") String userName);
	
}

新的测试方法,我要经过userName去作删除,删除不是真的删除,而是逻辑删除。

@Test
	public void deleteUser1(){
		userDao.deleteByUsername("root");
		System.out.println("删除成功");
	}

运行,就报错了:
org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query at ... ...

我靠,啥状况,我百度到的啊,怎么会错呢。

算了算了,这个不行,就换另外一种方法。

兔子:“小伙子,你这样可不行啊,你好歹看下报啥错啊。。。”

“额,好吧,我看下哈!咦,这个好熟悉,TransactionRequiredException,Transaction这个单词的意思好像是那个事物吧。Required是须要的意思,莫非报错的意思是,让我加一个事物,是这样嘛?”

兔子:“别问我啊,你本身试一下不就知道了嘛!”

“好吧,我就加一个事物的注解看看。奇怪了,我明明百度的文章,哎。”

兔子:“事物通常是加在service方法里面的,你别加在dao里面啊。你想直接测试dao层的方法,这个想法没有错,不过你最好仍是弄个service。”

“你的意思是,我再加一个service方法,加上事物,而后调用dao的方法?”

兔子:“嗯,或者你直接把事物加在test方法,也行的。”

说罢,兔哥帮我加上了注解,而后测试,居然经过了。

“这么说,我找的那篇文章,其实也是对的,等下奥,我翻下连接。”

兔子:“嗯,我看看。”

“就是这个...”

地址:https://blog.csdn.net/qq_33405420/article/details/89469293

兔子:“这写的没问题啊,只是别人不知道你这么菜而已,他也不知道你直接在junit测试类里面去测试dao的方法,并且还不加事物。”

“。。。好吧,那我后面创建service方法的时候,必定加上事物。”

兔子:“没事,我刚开始也这样,慢慢来就行了。对了,我已经把公众号的名字改为了【java小白翻身】,记得关注哦~”

4. 试试jpa的其余修改用法

话说这JPA还真好用,基本的增删改查我都不用写一句sql,对于一些复杂的业务逻辑,我也能够本身写jpql语句(其实仍是sql语句,算是面向对象的sql语句吧)。

接下来,我就试试别的方法。刚才已经插入了一条数据,如今我再用另外一种方法去修改数据,好比,我把密码改为123吧。

@Test
	public void modifyPassword(){
		User user = new User();
		user.setUserName("root");
		user.setPassword("123");
		userDao.saveAndFlush(user);
		System.out.println("修改为功");
	}

运行,结果崩了...

其余的数据全没了,看来这种更新是全量的更新,不是增量的。我还觉得他会只更新userName和password呢,看来是我太天真了。不怕,我再运行一下adduser测试方法,数据不就回来了嘛。。

我太机智了。

再运行一次,数据果真回来了。

这回得当心一点了,我先根据主键userName去拿到这个用户,而后再修改密码:

@Test
	public void modifyPassword(){
		User user = new User();
		user.setUserName("root");
		
		//先找到userName为root的用户
		user = userDao.findOne("root");
		//修改密码
		user.setPassword("123");
		
		userDao.saveAndFlush(user);
		System.out.println("修改为功");
	}

这下子就成功了。

5. JPA的奇技淫巧

springDataJpa还实现了一个很是牛逼的东西,就是根据方法名自动进行sql查询。
好比,我想根据roleId去作查询,就能够直接写一个方法:

public List<User> findByRoleId(String roleId);

SpringData JPA方法命名规则查询

顾名思义,方法命名规则查询就是根据方法的名字,就能建立查询。

只须要按照Spring Data JPA提供的方法命名规则定义方法的名称,就能够完成查询工做。

Spring Data JPA在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询

按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字链接,

要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,而后对剩下部分进行解析。

咱们多造一点测试数据,用addUser方法。

好比,咱们如今要查询nickName里面带有“剽悍”的,就用like。

public List<User> findByNickNameLike(String nickName);

测试:

@Test
	public void userQuery(){
		List<User> users = userDao.findByNickNameLike("%剽悍%");
		for (int i = 0; i < users.size(); i++) {
			System.out.println(users.get(i).getNickName());
		}
	}

结果:

这个算是jpa里面一个颇有意思的用法了,可是我感受这样内心好没底啊,哈哈。最稳妥的办法,仍是直接写JPQL语句吧。

好比,我这样写:

@Query("select u from User u where userName = ?1 and password = ?2")
public User findByUserNameAndPassword(String userName,String password);

这样的好处就是,你想写什么查询就写什么查询,是最放心的。

JPA里面最经常使用的两种传参方式,就是这两种。

其实,jpa里面还有不少其余的拼接方法,可是我看来看去,都太麻烦了,仍是直接用JPQL最省事。对于简单的查询,就直接用默认的方法便可,复杂的查询,就老老实实本身写sql吧。

相关文章
相关标签/搜索