自学内容网 自学内容网

SpringBoot的数据访问

Docker

CentOS安装命令

sudo yum update
sudo yum install docker

Ubuntu安装命令

sudo apt-get update
sudo apt-get docker.io

Windows下安装

windows运行Docker是通过Boot2Docker(https://github.com/boot2docker/windows-installer/releases/latest)实现的
这个软件包含一个VirtualBox,只适合开发测试,不适合生产环境

安装前确认电脑的BIOS设置中的CPU虚拟化技术支持已经开启

Docker的镜像都是放在Docker官网https://registry.hub.docker.com

docker search redis  # 检索redis

docker pull redis # 下载redis镜像

docker images #查看本地镜像列表

docker rmi image-id #删除指定镜像

docker rmi ${docker images -q} #删除所有镜像


docker run --name test-redis -d redis #运行一个Redis容器

docker ps #查看运行中的容器列表

docker ps -a #查看运行和停止状态的容器

docker stop test-redis #停止redis容器

docker start test-redis #启动redis容器

docker rm container-id #删除指定id的容器

docker rm $(docker ps -a -q) #删除所有容器

docker logs container-name/container-id #查看当前容器日志
#例如查看redis容器的日志
docker logs port-redis
# 映射容器的6379端口到本机的6378端口
docker run -d -p 6378:6379 --name port-redis redis

docker exec -it container-id/container-name bash #登录并访问当前容器,通过exit命令退出登录

# 将容器的Oracle XE管理界面的8080端口映射为本机的9090端口,将Oracle XE的1521端口映射为 本机的1521端口
docker run -d -p 9090:8080 -p 1521:1521 wnameless/oracle-xe-llg

容器:
hostname:localhost
端口:1521
SID:XE
username:system/sys
password:oracle

管理界面:
url: http://localhost:9090/apes
workspace:internal
username:admin
password:oracle

Spring Data JPA

属于Spring Data的一个子项目,通过提供基于JPA的Repository减少了JPA作为数据访问方案的代码量

①、继承JpaRepository意味着默认有了数据访问的操作方法

public interface PersonRepository extends JpaRepository<Person,Long>{
//定义数据访问操作的方法
}

②、配置使用Spring Data JPA

@Configuration
@EnableJpaRepositories("com.wisely.repos")//开启JPA,并扫描包下接口
public class JpaConfiguration{

@Bean
public EntityManagerFactory entityManagerFactory(){

}
}

③、定义查询方法

表——实体类

Ⅰ、根据属性名查询

public interface PersonRepository extends JpaRepository<Person,Long>{

//select p from Person p where p.name=?1
List<Person> findByName(String name);

//select p from Person p where p.name like ?1
List<Person> findByNameLike(String name);

//select p from Person p where p.name=?1 and p.address=?2
List<Person> findByNameAndAddress(String name,String address);

//获得符合查询条件的钱10条数据
List<Person> findFirst10ByName(String name);

//获得符合查询条件的前30条数据
List<Person> findTop30ByName(String name);
}

Ⅱ、使用JPA的NamedQuery查询

即一个名称映射一个查询语句

@Entity
@NamedQuery(name="Person.findByName",query="select p from Person p where p.name=?1")
public class Person{

}
public interface PersonRepository extends JpaRepository<>{

//使用NamedQuery里定义的查询语句,而不是根据方法名查询
List<Person> findByName(String name);
}

Ⅲ、使用@Query查询


public interface PersonRepository extends JpaRepository<Person,Long>{

//使用参数索引查询
@Query("select p from Peron p where p.address=?1")
List<Person> findByAddress(String address);

//使用命名参数
@Query("select p from Person p where p.address=:address")
List<Person> findByAddress(@Param("address") String address);

//@Modifying和@Query组合注解来更新查询,返回值表示影响的行数
@Modifying
@Transactional
@Query("update Person p set p.name=?1")
int setName(String name);
}

Ⅳ、Specification规范接口构造条件查询

//接口必须实现JpaSpecificationExecutor
public interface PersonRepository extends JpaRepository<Person,Long>,JpaSpecificationExecutor<Person>{

}
//定义Criterial查询
public class CustomerSpecs{

public static Specification<Person> personFromHefei(){
//使用Root获取需要查询的属性,查询所有来自合肥的人
@Override
public Predicate toPredicate(Root<Person> root,CriteriaQuery<?> query,CriteriaBuilder cb){
return cb.equal(root.get("address"),"合肥");
}
}
}
List<Person> people = personRespository.findAll(personFromHefei());

Ⅴ、排序与分页

//定义
public interface PersonRepository extends JpaRepository<Person,Long>{

List<Person> findByName(String name,Sort sort);
Page<Person> findByName(String name,Pageable pageable);
}
//使用排序
List<Person> people = personRepository.findByName("xx",new Sort(Direction.ASC,"age"));
Sort(Direction.ASC,"age")
//使用分页(获得当前页面的记录、总页数、总记录数、是否有上一页或下一页)
Page<Person> people2 = personRespository.findByName("xx",new PageRequest(0,10));

一、新建项目

依赖:spring-boot-starter-data-jpa spring-boot-starter-web ojdbc6 guava(包含Java常用工具类)

Maven中心库没有OracleJDBC驱动,需要通过Maven进行打包到本地仓库
Oracle官网下载ojdbc6.jar 通过控制台命令mvn install:install-file -DgroupId=com.oracle “-DartifactId=ojdbc6” -Dversion=11.2.0.2.0" “-Dpackaging=jar” “-Dfile=E:\odjbc6.jar”

  • -DgroupId=com.oracle 指定当钱包的groupId为com.oracle
  • -DartifactId=odjbc6 指定当前包的artifactfactId为ojdbc6
  • -Dversion=11.2.0.2.0 指定当钱包version为11.2.0.2.0
  • -Dfile=E:\odjbc6.jar 指定要打包的jar文件位置

新建一个data.sql放置在src/main/resources下,内容向表中增加一些数据

二、配置基本属性

appliation.properties里配置数据源和jpa的相关属性

spring.datasource.driverClassName=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc\:oracle\:thin\:@localhost\:1521\:xe
spring.datasource.username=boot
spring.datasource.password=boot

#hibernate提供根据实体类自动维护数据表结构的功能(create create-drop update validate none)
spring.jpa.hibernate.ddl-auto=update
#控制台现实sql
spring.jpa.show-sql=true
#控制器输出json字符串格式
spring.jackson.serialization.indent_output=true

三、定义实体类映射

@Entity //指明实体类
@NamedQuery(name="Person.withNameAndAddressNamedQuery",query="select p from Person p where p.name=?1 and address=?2")
public class Person {

@Id//指明这个属性映射为数据库的主键
@GeneratedValue//默认主键生成方式为自增,hibernate会自动生成一个名为HIBERNATE_SEQUENCE的序列
private Long id;

private String name;

private Integer age;

private String address;

//省略无参、有参构造和get/set方法
}

四、数据访问接口

public interface PersonRepository extends JpaRepository<Person,Long>{

List<Person> findByAddress(String name);//使用方法名查询

Person findByNameAndAddress(String name,String address);

//使用@Query查询
@Query("select p from Person p where p.name=:name and p.address=:address")
Person withNameAndAddressQuery(@Param("name")String name,@Param("address")String address);

//使用@NamedQuery查询,实体类中做@NamedQuery定义
List<Person> withNameAndAddressNameQuery(String name,String address);
}

五、控制器

@RestController
public class DataController{

//spring data JPA自动注册bean
@Autowired
PersonRepository personRepository;

@RequestMapping("/save")
public Person save(String name,String address,Integer age){
Person p = personRepository.save(new Person(null,name,age,address));
return p;
}

@RequestMapping("/q1")
public List<Person> q1(String address){
List<Person> people = personRepository.findByAddress(address);
return people;
}

@RequestMapping("/q2")
public Person q2(String name,String address){
Person people = personRepository.findByNameAndAddress(name,address);
return people;
}

@RequestMapping("/q3")
public Person q3(String name,String address){
Person p =personRepository.withNameAndAddressQuery(name,address);
return p;
}

@RequestMapping("/q4")
public Person q4(String name,String address){
Person p = personRepository.withNameAndAddressNamedQuery(name,address);
return p;
}

@RequestMapping("/sort")
public List<Person> sort(){
List<Person> people = personRepository.findAll(new Sort(Direction.ASC,"age"));
return people;
}

@RequestMapping("/page")
public Page<Person> page(){
Page<Person> pagePeople = personRepository.findAll(new PageReqeust(1,2));
return pagePeople;
}
}

六、自定义Repository实现自动模糊查询

①、定义Specification

public class CustomerSpecs{

public static <T> Specification<T> byAuto(final EntityManager entityManager,final T example){//

final Class<T> type = (Class<T>) example.getClass();//

return new Specification<T>(){
@Override
public Predicate toPredicate(Root<T> root,CriteriaQuery<?> query,CriteriaBuilder cb){
List<Predicate> predicates = new ArrayList<>();//
EntityType<T> entity = entityManager.getMetaModel().entity(type);//

for(Attribute<T,?> attr:entity.getDeclaredAttributes()){//
Object attrValue = getValue(example,attr);//
if(attrValue != null){
if(attr.getJavaType() == String.class){//
if(!StringUtils.isEmpty(attrValue)){//
predicates.add(cb.like(root.get(attriute(entity,attr.getName(),String.class)),
pattern((String)strValue)));//
}
}else{
predicates.add(cb.equal(root.get(attribute(entity,attr.getName(),attrValue.getClass())),attrValue));
}
}
}
return predicates.isEmpty()?cb.conjunction():cb.and(toArray(predicates,Predicate.class));//
}

private <T> Object getValue(T example,Attribute<T,?> attr){
return ReflectionUtils.getField((Field) attr.getJavaMember(),example);
}

//
private<E,T> SingularAttribute<T,E> attribute(EntityType<T> entity,String filedName,Class<E> fieldClass){
return entity.getDeclaredSingularAttribute(filedName,fieldClass);
}
};
}

//
static private String pattern(String str){
return "%" + str + "%";
}
}

②、定义接口

@NoRepositoryBean
public interface CustomRepository<T,ID extends Serializable> extends JpaRepository<T,ID>,JpaSpecificationExecutor<T>{
Page<T> findByAuto(T example,Pageable pageable);
}

③、定义实现

public class CustomRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID>
implements CustomRepository<T,ID>{

private final EntityManager entityManager;
public CustomRepositoryImpl(Class<T> domainClass,EntityManager entityManager){
super(domainClass,entityManager);
this.entityManager = entityManager;
}

@Override
public Page<T> findByAuto(T example,Pageable pageable){//构造查询条件,提供分页功能
return finalAll(byAuto(entityManager,example),pageable);
}
}

④、repositoryFactoryBean

public class CustomRepositoryFactoryBean<T extends JpaRepository<S,I>,S,ID extends Serializable>
extends JpaRepositoryFactoryBean<T,S,ID>{

@Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager){
return new CustomRepositoryFactory(entityManager);
}

private static class CustomRepositoryFactory extends JpaRepositoryFactory{
public CustomRepositoryFactory(EntityManager entityManager){
super(entityManager);
}

@Override
@SupprssWarnings({"unchecked"})
protected <T,ID extends Serializable> SimpleJpaRepository<?,?> getTargetRepository(RepositoryInformation information,
EntityManager entityManager){
return new CustomRepositoryImpl<T,ID>((Class<T>)information.getDomainType(),entityManager);
}

@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata){
return CustomRepositoryImpl.class;
}
}
}

⑤、使用

只需要让实体类Repository继承自定义的Repository接口,即可使用自定义Repository中实现的功能

public interface PersonRepository extends CustomRepository<Person,Long>{

List<Person> findByAddress(String address);
Person findByNameAndAddress(String name,String address);

@Query("select p from Person p where p.name=:name and p.address=:address")
Person withNameAndAddressQuery(@Param("name") String name,@Param("address") String address);

Person withNameAndAddressNamedQuery(String name,String address);
}

在实体类定义的数据类型要用包装类型(Long,Integer),而不能使用原始数据类型(long ,int)
因为在SpringMVC中使用原始数据类型会自动初始化为0,而不是空,导致构造条件失败

@RestController
public class DataController{

@RequestMapping("/auto")
public Page<Person> auto(Person person);

Page<Person> pagePeople = personRepository.findByAuto(person,new PageRequest(0,10));
return pagePeople;
}

⑥、配置

如果不需要自定义Repository实现,则在Spring Data JPA里无须添加@EnableJpaRepositories注解
因为@SpringBootApplication饱含了@EnableAutoConfiguration注解开启了对Spring Data JPA的支持

@SpringBootApplicaiton
//自定义的Repository实现起效
@EnableJpaRepositories(repositoryFactoryBeanClass=CustomRepositoryFactoryBean.class)
public class Ch82Application{

@Autowired
PersonRepository personRepository;

public static void main(String[] args){
SpringApplication.run(Ch82Application.class,args);
}
}

运行http://localhost:8080/auto无构造查询全部
http://localhost:8080/auto?address=肥,构造address的like查询

Spring Data REST

①、依赖

spring-boot-starter-data-jpa spring-boot-starter-data-rest

②、application.properties中配置属性

spring.data.rest.base-path:java.net.URI
spring.data.rest.default-page-size:int
spring.data.rest.limit-param-name:String
sprig.data.rest.max-page-size:int
spring.data.rest.page-param-name:String
spring.data.rest.return-body-on-create:boolean
spring.data.rest.return-body-on-update:boolean

③、实体类

@Entity
public class Person{
@Id
@GeneratedValue
private Long id;

private String name;

private Integer age;

private String address;

//有参构造,无参构造,get/set方法省略
}

④、实体类的Repository

public interface PersonRepository extends JpaRepository<Person,Long>{

Person findByNameStartsWith(String name);
}

⑤、安装Chrome插件Postman REST Client

Postman是一个支持REST的客户端,测试REST资源

声明式事务

①、依赖

spring-boot-starter-data-jpa spring-boot-starter-web

②、配置相关属性(略)

③、实体类(Person略)

④、实体类Repository

public interface PersonRepository extends JpaRepository<Person,Long>{

}

⑤、Service业务

public interface DemoService{
public Person savePersonWithRollBack(Person person);
public Person savePersonWithoutRollBack(Person person);
}
@Service
public class DemoServiceImpl implements DemoService{

@Autowired
PersonRepository personRepository;

//指定特定的异常,数据回滚,数据库不会新增
@Transactional(rollbackFor={IllegalArgumentException.class})
public Person savePersonWithRollBack(Person person){

Person p = personRepository.save(person);

if(person.getName().equals("jordan")){
//硬编码手动出发异常
throw new IllegalArgumentException("Jordan,已存在,数据将回滚");
}
return p;
}

//虽然抛出异常,但数据并没有回滚
@Trancational(noRollbackFor={IllegalArgumentException.class})
public Person savePersonWithoutRollBack(Person person){

Person p = personRepository.save(person);

if(person.getName().equals("jordan")){
throw new IllegalArgumentException("Jordan,已存在,数据不会回滚");
}
return p;
}

}

数据缓存

Spring提供四个注解声明缓存规则

  • @Cacheable 方法执行前先判断缓存中是否有数据,如果有直接返回缓存,如果没有调用方法的返回值放进缓存
  • @CachePut 无论怎样,都会将方法的返回值放进缓存
  • @CacheEvict 将一条或多条数据从缓存中删除
  • @Caching 可以通过@Caching注解组合多个注解策略在一个方法上

其中@Cacheable/@CachePut/@CacheEvit都有value属性
指定的是要使用缓存名称,key属性指定的是数据在缓存中的存储的键

①、依赖

spring-boot-starter-cache
spring-boot-starter-data-jpa
spring-boot-starter-web

②、实体类Person略

③、实体类Repository

public interface PersonRepository extends JpaRepository<Person,Long>{

}

④、业务接口以及实现类

public interface DemoService{

public Person save(Person person);
public void remove(Long id);
public Person findOne(Person person); 
}
@Service
public class DemoServiceImpl implements DemoService{

@Autowired
PersonRepository personRepository;

@Override
@CachePut(value="people",key="#person.id")//缓存新增或更新,缓存名称为people,数据key是person的id
public Person save(Person person){
Person p = personRepository.save(person);
System.out.println("为id、key为:"+p.getId()+"数据做了缓存");
return p;
}

@Override
@CacheEvict(value="people")//从缓存中删除key为id的数据
public void remove(Long id){
System.out.println("删除了id、key为"+id+"的数据缓存");
personRepository.delete(id);
}

@Override
@Cacheable(value="people",key="#person.id")//缓存key为person的id数据到缓存people
public Person findOne(Person person){
Person p = personRepository.findOne(person.getId());
System.out.println("为id、key为:"+p.getId()+"数据做了缓存");
return p;
}
}

⑤、开启缓存支持

@SpringBootApplication
@EnableCaching //开启缓存支持
public class ChApplication{
//略
}

第一次调用方法查询数据库会有sql语句在控制台
第二次没有sql,直接从缓存中拿数据

切换缓存技术

使用EhCache作为缓存技术

①、依赖

ehcache

EhCache需要的配置文件ehcache.xml放在 类路径下,SpringBoot自动扫描
SpringBoot会自动配置EhCacheCacheManager的Bean

<ehcache>
<cache name="people" maxElementsInMemory="1000"></ehcache>
</ehcache>

使用Guava作为缓存技术

①、依赖

guava

SpringBoot会自动配置GuavaCacheManager这个Bean

使用Redis作为缓存技术

①、依赖

spring-boot-starter-redis

SpringBoot自动配置RedisCacheManager以及RedisTemplate的Bean

非关系型数据库NoSQL

不适用SQL语言作为查询语言,数据存储也不是固定的表、字段
主要有文档存储型(MongoDB)、图形关系存储型(Neo4j)和键值对存储型(Redis)

MongoDB

①、安装MongoDB(其数据库管理软件Robomongo)

docker run -d -p 27017:27017 mongo

引入依赖spring-boot-starter-data-mongodb

②、配置信息

spring.data.mongodb.host=#
spring.data.mongodb.port=27017
spring.data.mongodb.uri=mongodb://localhost/test
spring.data.mongodb.database=
spring.data.mongodb.authentication-database=
spring.data.mongodb.grid-fs-database=
spring.data.mongodb.username=
spring.data.mongodb.password=
spring.data.mongodb.repositories.enabled=true #repository是否支持开启,默认为开启
spring.data.mongodb.field-naming-strategy=
org.springfraework.boot.autoconfigure.data.mongo

③、实体类

@Document //注解映射模型和MongoDB的文档
public class Person{

@Id //文档ID
private String id;
private String name;
private Integer age;
@Field("locs") //此属性在文档中的名称locs
private Colleciton<Location> locations = new LinkedHashSet<>();

//省略构造方法和get/set方法
}
public class Location{

private String place;

private String year;

//省略构造方法和get/set方法
}

④、数据访问

public interface PersonRepository extends MongoRepository<Person,String>{

Person findByName(String name);//支持方法名查询

@Query("{'age':?0}")//查询擦书构造JSON字符串即可
List<Person> withQueryFindByAge(Integer age);
}

⑤、Controller控制器

@RestController
public class DataController{

@Autowired
PersonRepository personRepository;

@RequestMapping("/save")
public Person save(){
Person p = new Person("wyf",32);
Collection<Location> locations = new LinkedHashSet<>();
Location loc1 = new Location("上海","2009");
Location loc2 = new Location("合肥","2010");
Location loc3 = new Location("广州","2011");
Location loc4 = new Location("马鞍山","2012");
locations.add(loc1);
locations.add(loc2);
locations.add(loc3);
locations.add(loc4);
p.setLocations(locations);
return personRepository.save(p);

}

@RequestMapping("/q1") //
public Person q1(String name){
return personReository.findByName(name);
}

@RequetMapping("/q2") //
public List<Person> q2(Integer age){
return personRepository.withQueryFindByAge(age);
}
}

Redis

根据Redis不同的Java客户端,Spring Data Redis提供了如下ConnecitonFactory

  • JedisConnectionFactory 使用Jedis作为Redis客户端
  • JredisConnectionFactory 使用Jredis作为Redis客户端
  • LettuceConnectionFactory 使用Lettuce作为Redis客户端
  • SrpConnectionFactory 使用Spullara/redis-protocol作为Redis客户端

①、安装Redis(使用Redis Client管理数据)

docker run -d -p 6379:6379 redis:2.8.21

依赖spring-boot-starter-redis spring-boot-starter-web

②、配置

spring.redis.database=0
spring.redis.host=localhost
spring.redis.password=
spring.redis.port=6379
spring.redis.pool.max-idle=8 #连接池设置
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.sentinel.master=
spring.redis.sentinel.nodes=
spring.redis.timeout=

③、模型bean

此类必须用时间序列化接口,因为使用Jaskson做序列化需要一个空构造

public class Person implements Serializable{

private static final long serialVersionUID = 1L;

private String id;
private String name;
private Integer age;

//构造函数,set/get方法
}

④、数据访问

@Repository
public class PersonDao{

@Autowired
StringRedisTemplate stringRedisTemplate;//SpringBoot已经配置了StringRedisTemplate,可以直接注入

@Resource(name="stringRedisTemplate")//注入基于字符串的简单属性操作
ValueOperations<String,String> valOpsStr;

@Autowired
RedisTemplate<Object,Object> redisTemplate;//SpringBoot已经配置了RedisTempalte

@Resource(name="redisTemplate")
ValueOperations<Object,Object> valOps;//

public void stringRedisTemplateDemo(){//存储字符串类型
valOpsStr.set("xx","yy");
}

public void save(Person person){
valOps.set(person.getId(),person);//存储对象类型
}

public String getString(){
return valOpsStr.get("xx");//获得字符串
}

public Person getPerson(){
return (Person) valOps.get("1");//获得对象
}
}

⑤、配置

SpringBoot自动配置了RedisTemplate,而RedisTemplate使用的是JdkSerializationRedisSerializer(二进制形式存储数据)

这里自定义配置RedisTemplate并定义Serializer

@SpringBootApplication
public class ChApplication{

public static void main(String[] args){
SpringApplication.run(ChApplication.class,args);
}

@Bean
@SuppressWarnings({"rawtypes","unchecked"})
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException{
RedisTemplate<Object,Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);

Jaskson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jaskson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(Propertyccessor.ALL,JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);

//设置值value的序列化采用Jackson2JsonRedisSerializer
template.setValueSerializer(jackson2JsonRedisSerializer);//
//设置键key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());

template.aftrPropertiesSet();
return template;
}
}

⑥、控制器

@RestController
public class DataController{

@Autowired
PersonDao personDao;

@RequestMapping("/set")//设置字符及对象
public void set(){
Person person = new Person("1","wyf",32);
personDao.save(person);
personDao.stringRedisTemplateDemo();
}

@RequestMapping("/getStr")//获得字符
public String getStr(){
return personDao.getString();
}

@RequestMapping("/getPerson")//获得对象
public Person getPerson(){
return personDao.getPerson();
}
}

原文地址:https://blog.csdn.net/usa_washington/article/details/142596999

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!