Docker
CentOS安装命令
bash
sudo yum update
sudo yum install docker
Ubuntu安装命令
bash
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
bash
docker search redis # 检索redis
docker pull redis # 下载redis镜像
docker images #查看本地镜像列表
docker rmi image-id #删除指定镜像
docker rmi ${docker images -q} #删除所有镜像
bash
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
bash
# 映射容器的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意味着默认有了数据访问的操作方法
java
public interface PersonRepository extends JpaRepository<Person,Long>{
//定义数据访问操作的方法
}
②、配置使用Spring Data JPA
java
@Configuration
@EnableJpaRepositories("com.wisely.repos")//开启JPA,并扫描包下接口
public class JpaConfiguration{
@Bean
public EntityManagerFactory entityManagerFactory(){
}
}
③、定义查询方法
表------实体类
Ⅰ、根据属性名查询
java
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查询
即一个名称映射一个查询语句
java
@Entity
@NamedQuery(name="Person.findByName",query="select p from Person p where p.name=?1")
public class Person{
}
java
public interface PersonRepository extends JpaRepository<>{
//使用NamedQuery里定义的查询语句,而不是根据方法名查询
List<Person> findByName(String name);
}
Ⅲ、使用@Query查询
java
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规范接口构造条件查询
java
//接口必须实现JpaSpecificationExecutor
public interface PersonRepository extends JpaRepository<Person,Long>,JpaSpecificationExecutor<Person>{
}
java
//定义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"),"合肥");
}
}
}
java
List<Person> people = personRespository.findAll(personFromHefei());
Ⅴ、排序与分页
java
//定义
public interface PersonRepository extends JpaRepository<Person,Long>{
List<Person> findByName(String name,Sort sort);
Page<Person> findByName(String name,Pageable pageable);
}
java
//使用排序
List<Person> people = personRepository.findByName("xx",new Sort(Direction.ASC,"age"));
Sort(Direction.ASC,"age")
java
//使用分页(获得当前页面的记录、总页数、总记录数、是否有上一页或下一页)
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的相关属性
shell
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
三、定义实体类映射
java
@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方法
}
四、数据访问接口
java
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);
}
五、控制器
java
@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
java
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 + "%";
}
}
②、定义接口
java
@NoRepositoryBean
public interface CustomRepository<T,ID extends Serializable> extends JpaRepository<T,ID>,JpaSpecificationExecutor<T>{
Page<T> findByAuto(T example,Pageable pageable);
}
③、定义实现
java
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
java
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中实现的功能
java
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,而不是空,导致构造条件失败
java
@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的支持
java
@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中配置属性
bash
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
③、实体类
java
@Entity
public class Person{
@Id
@GeneratedValue
private Long id;
private String name;
private Integer age;
private String address;
//有参构造,无参构造,get/set方法省略
}
④、实体类的Repository
java
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
java
public interface PersonRepository extends JpaRepository<Person,Long>{
}
⑤、Service业务
java
public interface DemoService{
public Person savePersonWithRollBack(Person person);
public Person savePersonWithoutRollBack(Person person);
}
java
@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
java
public interface PersonRepository extends JpaRepository<Person,Long>{
}
④、业务接口以及实现类
java
public interface DemoService{
public Person save(Person person);
public void remove(Long id);
public Person findOne(Person person);
}
java
@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;
}
}
⑤、开启缓存支持
java
@SpringBootApplication
@EnableCaching //开启缓存支持
public class ChApplication{
//略
}
第一次调用方法查询数据库会有sql语句在控制台
第二次没有sql,直接从缓存中拿数据
切换缓存技术
使用EhCache作为缓存技术
①、依赖
ehcache
EhCache需要的配置文件ehcache.xml放在 类路径下,SpringBoot自动扫描
SpringBoot会自动配置EhCacheCacheManager的Bean
xml
<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
②、配置信息
bash
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
③、实体类
java
@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方法
}
java
public class Location{
private String place;
private String year;
//省略构造方法和get/set方法
}
④、数据访问
java
public interface PersonRepository extends MongoRepository<Person,String>{
Person findByName(String name);//支持方法名查询
@Query("{'age':?0}")//查询擦书构造JSON字符串即可
List<Person> withQueryFindByAge(Integer age);
}
⑤、Controller控制器
java
@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
②、配置
shell
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做序列化需要一个空构造
java
public class Person implements Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String name;
private Integer age;
//构造函数,set/get方法
}
④、数据访问
java
@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
java
@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;
}
}
⑥、控制器
java
@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();
}
}