SpringData简单梳理

SpringData简单梳理

作者:李晶晶;

日期;2021年3月12日;

其实好几天之前这几个demo就写好了,但一直没时间写篇文档来总结一下,事情有点多,人也有点累。

SpringData是一个持久层的通用解决方案,目的是统一不同持久层的操作api。

原来比如MySql,redis他们需要提供一套api让Java程序员使用。后来SpringData就给出了一套规范(一组接口),让那些厂家实现这组规范。这样对于程序员来说,无论持久层采取什么方案,代码写起来都差不多。

这篇文档会给出使用SpringDataJpa、SpringDataRedis、SpringDataElasticSearch、SpringDataMongoDB的简单使用。

demo都是基于SpringBoot的而不是Spring。

Jpa

Jpa也是一套规范,这里就不展开说了,它实现了不同数据库之间的切换,代码不用动。因为它根本就不在代码里写sql(不同于MyBatis)。

Jpa的实现厂家有很多,但是比较常用的就是Hibernate的Jpa实现。

依赖

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

<!-- 数据库这里就以mysql为例了 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

配置

spring: 
  datasource: 
    driver-class-name: com.mysql.cj.jdbc.Driver
    name: defaultDataSource
    url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: lijingjing
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

实体类

实体类与表结构的对应可以通过注解配置。

package com.ljj.pojo;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "user")
public class User {
	@Id//主键
	@GeneratedValue(strategy = GenerationType.IDENTITY)//自增
	@Column(name = "id")//若字段名与属性名一致可省略
	private Integer id;
	private String name;
	private Integer age;
	private String sex;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", age=" + age + ", sex=" + sex + "]";
	}
}

Dao

package com.ljj.dao;

import org.springframework.data.jpa.repository.JpaRepository;

import com.ljj.pojo.User;

public interface UserDao extends JpaRepository<User, Integer>{

}

没错写一个空接口就行了,JpaRepository的最上面的那个接口是Repository注解,所以可以看到我们也不用自己来注入。

注意,我们是不需要写这个接口的实现的,实现都由Hibernate做好了的。

这里有一条继承关系:JpaRepository -> PagingAndSortingRepository -> CrudRepository -> Repository.

每个接口里都由很多方法可以使用。

JpaRepository中的泛型:第一个是实体类,第二个是主键类型;

我们其实也可以在接口中定义一些别的方法,只要按照Jpa的规则来就行,具体规则这里就不细说了,百度一下。

测试案例

下面试一下简单操作的测试demo

package com.ljj;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import com.ljj.dao.UserDao;
import com.ljj.pojo.User;

@SpringBootTest
class SpringDataJpaApplicationTest {

	@Autowired
	private UserDao userDao;

	@Test
	void select() {
		// 查询全部
		List<User> list = userDao.findAll();
		list.forEach(user -> System.out.println(user));

		// 查询单个
		Optional<User> u = userDao.findById(1);
		System.out.println(u.get());
	}

	@Test
	void insert() {
		// 插入一条
		User u = new User();
		u.setAge(12);
		u.setSex("man");
		u.setName("李晶晶");
		userDao.save(u);

		// 插入(没id)或修改(有id)多条
		User u1 = new User();
		u1.setAge(13);
		u1.setSex("man");
		u1.setName("李晶晶");

		User u2 = new User();
		u2.setAge(13);
		u2.setSex("man");
		u2.setName("李晶晶");

		User u3 = new User();
		u3.setAge(13);
		u3.setSex("man");
		u3.setName("李晶晶");

		List<User> list = new ArrayList<User>();
		list.add(u1);
		list.add(u2);
		list.add(u3);

		userDao.saveAll(list);
	}

	@Test
	void update() {
		Optional<User> u = userDao.findById(5);
		u.get().setAge(-1);
		userDao.save(u.get());
	}

	@Test
	void delete() {
		userDao.deleteById(5);
	}

}

可以看到我全程没写过一条sql,所以对于一些简单的业务,Jpa开发起来是真的快。

当然如果业务很复杂,其实没有直接写sql来的方便。

Repository可以看做是SpringData操作持久层的一种方式,但其实还有另一种方式Template,在接下的操作Redis中我们可以看到。

Redis

Redis一般用来做缓存,使用SpringData操作Redis同样是十分方便的。

依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置

spring:
  redis:
    database: 0 #默认就是0
    host: 127.0.0.1
    password: #默认就是空
    port: 6379

实体类

package com.ljj.pojo;

import java.io.Serializable;

public class Person implements Serializable{

	private static final long serialVersionUID = 1L;
	private int age;
	private String name;
	private String info;
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getInfo() {
		return info;
	}
	public void setInfo(String info) {
		this.info = info;
	}
	@Override
	public String toString() {
		return "Person [age=" + age + ", name=" + name + ", info=" + info + ", getAge()=" + getAge() + ", getName()="
				+ getName() + ", getInfo()=" + getInfo() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode()
				+ ", toString()=" + super.toString() + "]";
	}	
}

Redis就没什么表结构了,反正就那么几种数据类型。

要注意的是实体类需要继承Serializable结构,方便将对象序列化到Redis中。

测试案例

操作str

package com.ljj;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@SpringBootTest
public class StrTest {
	@Autowired
	private RedisTemplate<Object, Object> template;

	@BeforeEach
	void init() {
		System.out.println("初始化");
		template.setKeySerializer(new StringRedisSerializer());
		template.setValueSerializer(new StringRedisSerializer());
	}

	/**
	 * set操作
	 */
	@Test
	void setStr() {
		// 插入操作
		template.opsForValue().set("ccc", new String("aaa"));

		// 保存10秒
		template.opsForValue().set("name", "ljj", 10, TimeUnit.SECONDS);

		// 替换部分字符串
		template.opsForValue().set("ccc", "bc", 1);

		// 有了就不存储,没有才插入
		template.opsForValue().setIfAbsent("name", "muziliuri");

		// 批量插入
		Map<String, String> map = new HashMap<String, String>();
		for (int i = 0; i < 5; i++) {
			map.put(i + "", i + "");
		}
		template.opsForValue().multiSet(map);

		// 追加
		template.opsForValue().append("性别", "男");
	}

	/**
	 * get操作
	 */
	@Test
	void getStr() {
		// 获取操作
		System.out.println(template.opsForValue().get("ccc"));

		// 获取子串(闭区间)
		System.out.println(template.opsForValue().get("ccc", 0, 1));

		// 批量获取
		List<Object> list = new ArrayList<Object>();
		for (int i = 0; i < 5; i++) {
			list.add(i + "");
		}
		List<Object> values = template.opsForValue().multiGet(list);
		for (Object value : values) {
			System.out.println(value);
		}

		// 获取值的长度
		System.out.println(template.opsForValue().size("ccc"));
	}

	/**
	 * 自增自减操作
	 */
	@Test
	void incre() {
		// 自增1
		template.opsForValue().increment("0");

		// 自增5
		template.opsForValue().increment("1", 5);

		// 自减1
		template.opsForValue().decrement("2");

		// 自减5
		template.opsForValue().decrement("3", 5);
	}
}

操作hash

package com.ljj;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.ljj.pojo.Person;

@SpringBootTest
public class HashTest {

	@Autowired
	private RedisTemplate<String, String> template;

	private HashOperations<String, String, Person> ops;

	@BeforeEach
	void init() {
		template.setKeySerializer(new StringRedisSerializer());
		template.setHashKeySerializer(new StringRedisSerializer());
		template.setHashValueSerializer(new JdkSerializationRedisSerializer());

		this.ops = template.opsForHash();
	}

	@Test
	void put() {
		Person p = new Person();
		p.setAge(12);
		p.setInfo("程序员");
		p.setName("ljj");
		// 保存
		ops.put("people", "me", p);

//		ops.putAll(key, m);批量保存
//		ops.putIfAbsent(key, hashKey, value); 无责保存,有则不变

	}

	@Test
	void get() {
		// 是否有hashkey
		System.out.println(ops.hasKey("people", "me"));
		// 获取值
		System.out.println(ops.get("people", "me"));
		// 获取所有键
		Set<String> keys = ops.keys("people");
		// 获取所有值
		List<Person> values = ops.values("people");
		// 获取键值对
		Map<String, Person> entries = ops.entries("people");
	}

	@Test
	void delete() {
		ops.delete("people", "me");
	}

}

操作list

package com.ljj;

import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

@SpringBootTest
public class ListTest {

	@Autowired
	private RedisTemplate<String, String> template;
	private ListOperations<String, String> ops;

	@BeforeEach
	void init() {
		this.ops = template.opsForList();
	}

	@Test
	void add() {
		// 添加一个
		ops.leftPush("brand", "dell");
		// 添加多个
		ops.leftPushAll("brand", "hp", "lenovo");
	}

	@Test
	void find() {
		// 查询指定个数(负数表示从右往左查)
		ops.index("brand", 1);
		// 全找一遍
		List<String> range = ops.range("brand", 0, -1);
		range.forEach(item -> System.out.println(item));
	}

	@Test
	void delete() {
		// 弹出操作
		String rightPop = ops.rightPop("brand");
		System.out.println(rightPop);
	}
}

操作set

package com.ljj;

import java.util.List;
import java.util.Set;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;

@SpringBootTest
public class SetTest {

	@Autowired
	private RedisTemplate<String, String> template;
	private SetOperations<String, String> ops;

	@BeforeEach
	void init() {
		this.ops = template.opsForSet();
	}

	@Test
	void add() {
		// 添加
		ops.add("Integer", "一", "二", "一");
	}

	@Test
	void find() {
		// 查询所有
		String key = "Integer";
		Set<String> members = ops.members(key);
		members.forEach(e -> System.out.println(e));

		// 随机获取一个
		String randomMember = ops.randomMember("Integer");
		System.out.println(randomMember);

		// 随机获取指定个元素
		List<String> randomMembers = ops.randomMembers("Integer", 2);
		randomMembers.forEach(e -> System.out.println(e));
	}

	@Test
	void delete() {
		Long remove = ops.remove("Integer", "一", "二", "三");
		System.out.println(remove);
	}
}

操作zset

package com.ljj;

import java.util.Set;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;

@SpringBootTest
public class ZsetTest {

	@Autowired
	private RedisTemplate<String, String> template;
	private ZSetOperations<String, String> ops;

	@BeforeEach
	void init() {
		this.ops = template.opsForZSet();
	}

	@Test
	void add() {
		// 添加成员
		ops.add("stu", "a", 23);
		ops.add("stu", "b", 24);
		ops.add("stu", "c", 25);
		// 增加元素score
		ops.incrementScore("stu", "a", 3);
	}

	@Test
	void find() {
		// 查询score
		Double score = ops.score("stu", "b");
		System.out.println(score);
		// 查询元素拍名
		Long rank = ops.rank("stu", "c");
		System.out.println(rank);
		// 根据拍名区间查询元素
		Set<String> range = ops.range("stu", 0, -1);
		range.forEach(e -> System.out.println(e));
	}

	@Test
	void delete() {
	}
}

通用操作

package com.ljj;

import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@SpringBootTest
public class AllTest {

	@Autowired
	private RedisTemplate<Object, Object> template;

	@BeforeEach
	void init() {
		System.out.println("初始化");
		template.setKeySerializer(new StringRedisSerializer());
		template.setValueSerializer(new StringRedisSerializer());
	}

	/**
	 * 删除操作
	 */
	@Test
	void delete() {
		// 删除
		template.delete("ccc");

		// 批量删除
		List<Object> list = new ArrayList<Object>();
		list.add("1");
		list.add("2");
		list.add("3");
		template.delete(list);

	}

}

这就是SpringData的另一种方案Template,他将一切操作都封装到一个template中,同样非常的方便。

ElasticSearch

es是一个全文搜索引擎,也能用来做存储,这里的案例使用Repository的方式来操作。

依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

配置

spring:
  elasticsearch:
    rest:
      uris: http://localhost:9200

这是最新的推荐的端口9200。

之前一直是用9300的,那个不是http协议的。现在SpringData支持了es的http端口,官网推荐用这个了。

实体类

@Document(indexName = "people")标明了索引名称;(在es7版本及以后,不再有type的概念,一个索引下就只有一个类型_doc).

@Field(type = FieldType.Keyword)表示了该字段的类型是keyword(不支持分词)。

package com.ljj.pojo;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Document(indexName = "people")
public class People {
	@Id
	private Integer id;

	@Field(type = FieldType.Keyword)
	private String name;

	@Field(type = FieldType.Keyword)
	private String identity;

	@Field(type = FieldType.Integer)
	private Integer age;

	@Field(type = FieldType.Double)
	private Double salary;

	@Field(type = FieldType.Text)
	private String info;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getIdentity() {
		return identity;
	}

	public void setIdentity(String identity) {
		this.identity = identity;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public Double getSalary() {
		return salary;
	}

	public void setSalary(Double salary) {
		this.salary = salary;
	}

	public String getInfo() {
		return info;
	}

	public void setInfo(String info) {
		this.info = info;
	}

	@Override
	public String toString() {
		return "People [id=" + id + ", name=" + name + ", identity=" + identity + ", age=" + age + ", salary=" + salary
				+ ", info=" + info + "]";
	}

}

Dao

package com.ljj.dao;

import java.util.List;

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import com.ljj.pojo.People;

public interface PeopleDao extends ElasticsearchRepository<People, Integer> {
    //info字段的类型是text,所以这个方法其实是全文检索
	List<People> findByInfo(String info);
}

测试案例

package com.ljj;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import com.ljj.dao.PeopleDao;
import com.ljj.pojo.People;

@SpringBootTest
class SpringDataElasticSearchApplicationTests {

	@Autowired
	private PeopleDao peopleDao;

	@Test
	void test() {
		Iterable<People> people = peopleDao.findAll();
		people.forEach(peo -> System.out.println(peo));
		System.out.println(peopleDao.findById(1));
		System.out.println(peopleDao.findByInfo("not"));
	}

}

MongoDB

依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

配置

spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/study

实体类

package com.ljj.pojo;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collation = "people")
public class People {
	@Id
	private Integer id;

	private String name;

	private String identity;

	private Double salary;

	private String info;

	private Integer age;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getIdentity() {
		return identity;
	}

	public void setIdentity(String identity) {
		this.identity = identity;
	}

	public Double getSalary() {
		return salary;
	}

	public void setSalary(Double salary) {
		this.salary = salary;
	}

	public String getInfo() {
		return info;
	}

	public void setInfo(String info) {
		this.info = info;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "People [id=" + id + ", name=" + name + ", identity=" + identity + ", salary=" + salary + ", info="
				+ info + ", age=" + age + "]";
	}

}

Dao

package com.ljj.dao;

import org.springframework.data.mongodb.repository.MongoRepository;

import com.ljj.pojo.People;

public interface PeopleDao extends MongoRepository<People, Integer>{

}

测试案例

package com.ljj;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import com.ljj.dao.PeopleDao;
import com.ljj.pojo.People;

@SpringBootTest
class SpringDataMongoDbApplicationTests {

	
	@Autowired
	private PeopleDao peopleDao;
	
    @Test
    void save() {
    	People p = new People();
    	p.setId(1);
    	p.setAge(15);
    	p.setIdentity("student");
    	p.setInfo("a good man");
    	peopleDao.save(p);
    }
    
    @Test
    void find() {
    	System.out.println(peopleDao.findById(1));
    }

}

跟上面的都差不多是吧,这就是SpringData的魅力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值