《Spring Boot2+Thymeleaf企业实战》读书笔记——数据库实战
不贴代码,仅作思路与要点记录,如果因为标题或关键词而点进来想要看代码解决实际问题的,建议右上角点× orz
以前老觉得应该看代码照抄代码,把代码找个地方复制粘贴保存起来以后需要的时候就Ctrl F一下就万事大吉,后来发现我不仅记不住原理和实际该怎么写,我连那些保存起来的代码放哪里了都不知道……于是就有了这个笔记。上次写笔记还是去年……想来我是生信既没学会多少,本职也给丢了个干净。这不太行,不,是很不行。
对应章节为《Spring Boot2+Thymeleaf企业实战》中第12章:数据库实战。
本章要点为:
- 介绍Spring Data
- Spring Data与JPA
- Spring Data与MongoDB
- Spring Data与Redis
以下为正经笔记
Spring Data 概述及其功能
为数据访问提供一个通用的模型,使不同类型的数据库可以通过同样的方式来访问和操作。在MVC三层架构里,直接对接的使DAO层,为其提供统一的数据访问模型。
它的功能主要是以下几个:
- 提供数据与对象映射的抽象层,同一个对象可以被映射成不同数据库的数据;
- 根据数据存储接口的方法名,自动实现数据查询;
- 为各个领域模型提供最基本的实现,例如普通的增删改查;
- 可在原有逻辑的基础上,实现自定义的数据库操作逻辑。
Spring Data 与JPA访问Mysql
大致步骤如下:
- 在pom.xml中引入spring-boot-starter-data-jpa的模块;
- 新建application.yml配置文件,以树形结构写入server/port,spring/datasource/driver-class-name、url、username、password等信息;
- 创建和表结构对应的实体类,使用 @Entity 注解和 @Table(name=“表名”) 注解。对id主键使用 @Id 和 @GeneratedValue(strategy=GenerationType.IDENTITY) 两个注解,getter和setter不要忘记生成;
- 新建一个接口继承JpaRepository<类名, Integer>;
- 新建一个Serveice业务逻辑类,在其中用 @Autowrited 注入前一步建立的接口;
- 写各种接口,比如get一个列表,就用这个注入的接口.findAll(),要保存数据就.save(对象);
到这一步,能实现都是因为Spring自动为这个继承了JpaRepository的接口生成了代理类,默认使用JDK动态代理。findAll()之类的都是原有的方法,这些自带的可以找文档查一查,或者这本书的这个章节里就有对应表格,在此不赘述。
如果要实现自己特殊的逻辑,就要按以下步骤:
- 新建一个接口,写一个任意命名的方法;
- 新建一个接口实现类,实现这个接口,并声明一个 EntityManager 类,给它加上 @PersistenceContext 注解;
- 在这个实现类里,重写接口里的方法,使用那个声明好的EntityManager实例,调用其createQuery(“sql语句里from及后面的部分”);
- 第三步执行出来的是Query对象,调用这个对象的getResultList()才是最终需要的结果。
这一步是以实现接口的方式完成的,所以对spring来说,他的自动扫描的就是被继承的接口名加上Impl后缀,如果扫描不到这样命名的类,就会将接口接口里的方法当作被查询表里的字段,然后就会喜闻乐见地报错。所以命名方式一定要遵守规则。
同理,如果想根据任意一个字段做 from table where name= 这样的查询,可直接在接口里写一个与要查询字段同名的方法。甚至可以直接带一些and或者between之类的关键字做查找。这个也是有表格可循。估计在eclipse和IDEA这样的IDE里都会有提示吧。
再如果,以上仍不能满足需求——而这是经常发生的——那么就要用到 @Query 注解。用法是在接口方法上加 @Query(“sql句子”) 。
其中,变量以?代替,还要加上序号,如 @Query(“select p from Person p where p.name = ?1”) 此时该方法里这个name是第一个参数。书上说,该注解也支持原生的SQL查询,举例是 @Query(value = “SELECT * FROM CRA_PERSON WHERE NAME = ?1”, nativeQuery = true) 。两个句子有点小区别,不过私以为实体类一开始就设置为与表名一致就可以避免记混。
Spring Data 与 MongoDB
书里讲的很细致,从MongoDB的历史到安装流程,这里就不赘述了,后面可能会找一些靠谱的安装博客贴个链接过来做备注。
MongDB的一些概念和MySQL还是有不小的区别的,下面做一些记录:
database:数据库,这个与SQL相同;
collection:数据集合,相当于SQL的数据表table;
document:数据文档,详单与SQL的一条数据;
filed:数据域,可看作SQL的列column;
index:数据索引。
使用步骤如下:
- 在pom.xml文件中引入spring-boot-starter-data-mongdb
- 新建application.yml文件,写各种配置,和MySQL的配置写法差不多,url换成uri,不需要username和password
- 在实体类中使用 @Document(collection = “表名”) 的注解,主键使用@Id
- 新建接口继承MongoRepository<表名, String>
- 创建service业务逻辑类,注入新建的接口,调用的方法如 findAll() 等,与MySQL一致
同样的上面只是最基础的应用,若要自定义数据存储逻辑,步骤如下:
- 新建一个接q’r’wo’ripe’r’pure’o’q’e’o’i口,写一个自定义方法;
- 新建一个service业务逻辑类,实现第一步的接口,注入一个MongoTemplate对象;
- 重写该方法。
这个重写略复杂,这里直接贴一下抄下来的代码
public class PersonRepImpl implements PersonRepCustom{
@Autowrited
private MongoTemplate mongoTemplate;
public List<Person> myQuery(){
List<Person> datas = mongoTemplate.execute(Person.class,
new CollectionCallback<List<Person>>(){
public List<Person> doInCollection(MongoCollection collection)throws MongoException, DataAccessException{
//查询全部数据
FindIterable fi = collection.find();
MongoCursor<Document> cursor = fi.iterator();
List<Person> result = new ArrayList<Person>();
while (cursor.hasNext()){
//获取源数据实例
Document source = cursor.next();
//转换为Person
Person p = new Person();
ObjectId objectId = (ObjectId)source.get("_Id");
p.setId(objectId.toHexString());
p.setAge((Integer)source.get("age"));
p.setName((String)source.get("name"));
p.setCompany((String)source.get("company"));
result.add(p)
}
return result;
}
});
return datas;
}
}
书里对这一段的解释是这样的:
在自定义的 myQuery 方法中,使用 MongoTemplate 来执行数据查询。在调用 execute 方法时,实现一个 CollectionCallback 接口,这样的使用方式,接触过 Spring 和 Hibernate 的读者应该非常熟悉。在实现 CollectionCallback 接口时,使用 MongoDB 的 MongoCollection 对象进行数据查询,本例查询 MongoCollection 的全部数据,并封装为 Person 集合返回。
MongoDB模块同样支持 findByAgeLessThan(Integer age) 这样的方法名查询方法,关键字也都可自行查询资料。
需要注意的是,MongolianDB有自己独特的查询操作符,如$gt等,这些可以查阅MongolianDB文档进行了解。
使用 @Query 注解来定义查询条件时,可参考以下两段代码,需要注意的是,这里的 @Query 注解与 JPA 的 @Query 注解是完全不同的两个东西,务必区分开。
@Query("{ 'name' : ?0, 'age' : ?1}")
List<Person> finByNameAndAge(String name, Integer age);
//这里为@Query注解添加了fileds字段,表示最终查询出来的Person只有id、name、company属性,其他的属性值为null
@Query(value = "{'name' : ?0}", fileds = "{'name':1,'company':1}")
List<Person> findByName(String name);
Spring Data 与 Redis
Redis,大名鼎鼎,就不多介绍了。安装步骤同样省略。
Redis 的数据类型这里记录一下。
string:最基本的数据类型,可保存任何数据;
hash:一个键值对的集合,在集合中以字段名作为key,字段值作为value,主要用于保存对象;
list:字符串列表,可以往列表中添加元素;
set:无序的集合,集合内数据不能重复;
zset:有序的集合,集合内数据不能重复。
在进行数据存储时要注意使用的数据类型,例如保存时用的hash类型,则获取数据时也要使用hash类型。
如果需要保存一些不会重复的数据,例如UUID,使用set类型来保存会比较理想,如果保存的是 java 对象,使用hash类型保存比较合适。Spring Data 默认使用 Jedis 来操作 Redis 。
下面先记录的是 Jedis 如何使用 hash 和 set 类型的数据。
- 引入Jedis依赖:groupId=redis.client,artifactId=jedis;
- 分别读写 hash 和 set 类型的数据
public class JetisTest(){
public static void main(String[] args){
//登录本地Redis
Jedis jedis = new Jedis("localhost");
jedis.auth("123456");
//将数据保存为hash类型;
Map<String,String> data = new HashMap<String,String>();
data.put("name","Angus");
data.put("age","33");
data.put("company","crazyit");
jedis.hmset("person_test",data);
//查询hash类型的数据
List<String> dbDatas = jesid.hmget("person_test","name","age");
for(String dbData : dbDatas){
System.out.println(dbData);
}
//将数据保存为set类型
jedis.sadd("person_test_ids","1","2");
//查询数据
Set<String> dbDataSet = jedis.smembers("person_test_ids");
for(String dbData : dbDataSet){
Sytem.out.println(dbData);
}
}
}
Spring Boot 同样提供了 Spring Data 与 Redis的依赖。使用步骤如下:
- 在pom文件中引入 spring-boot-starter-data-redis;
- 新建application.yml文件,配置 server/port 、 spring/redis/port、password、host(可改用 url:redis://ignore:123456@localhost:6379 的写法);
- 为项目建立controller、repository、service、entity包;
- 实体类中,在类外面加 @RedisHash(“类名”),这样就可以使这个类对象以 hash 类型被保存。对主键ID使用 @Id 注解,其会作为set类型被保存;对索引字段使用 @Indexed 注解;
- 接口与实现类的与前面 MySQL 和 Mongodb 的基本相同。不同的在于查询返回的对象是 Iterable 实例,需要便利后存到 List 中。
- 自定义数据存储逻辑也与前二者大同小异。