欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > SpringBoot的数据访问

SpringBoot的数据访问

2024/10/25 12:22:13 来源:https://blog.csdn.net/usa_washington/article/details/142596999  浏览:    关键词: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  # 检索redisdocker 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 redisdocker 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{@Beanpublic EntityManagerFactory entityManagerFactory(){}
}

③、定义查询方法

表——实体类

Ⅰ、根据属性名查询

public interface PersonRepository extends JpaRepository<Person,Long>{//select p from Person p where p.name=?1List<Person> findByName(String name);//select p from Person p where p.name like ?1List<Person> findByNameLike(String name);//select p from Person p where p.name=?1 and p.address=?2List<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获取需要查询的属性,查询所有来自合肥的人@Overridepublic 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@AutowiredPersonRepository 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>(){@Overridepublic 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;}@Overridepublic 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>{@Overrideprotected 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);}@Overrideprotected 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{@AutowiredPersonRepository 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@GeneratedValueprivate 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{@AutowiredPersonRepository 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{@AutowiredPersonRepository personRepository;@Override@CachePut(value="people",key="#person.id")//缓存新增或更新,缓存名称为people,数据key是person的idpublic 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数据到缓存peoplepublic 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 //文档IDprivate String id;private String name;private Integer age;@Field("locs") //此属性在文档中的名称locsprivate 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{@AutowiredPersonRepository 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{@AutowiredStringRedisTemplate stringRedisTemplate;//SpringBoot已经配置了StringRedisTemplate,可以直接注入@Resource(name="stringRedisTemplate")//注入基于字符串的简单属性操作ValueOperations<String,String> valOpsStr;@AutowiredRedisTemplate<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的序列化采用Jackson2JsonRedisSerializertemplate.setValueSerializer(jackson2JsonRedisSerializer);////设置键key的序列化采用StringRedisSerializertemplate.setKeySerializer(new StringRedisSerializer());template.aftrPropertiesSet();return template;}
}

⑥、控制器

@RestController
public class DataController{@AutowiredPersonDao 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();}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com