5 Nosql
5.1 nosql特点
Nosql为非关系型数据库,相比于传统的关系型数据库来说,在面对高并发时候会出现一些问题,非关系型数据库对于解决高并发来说,非常有优势。MongoDB是一个基于分布式文件存储的开源数据库系统。
为什么使用nosql:
- 对数据库高并发读写
- 对海量数据高效率存储和访问
- 对数据库的高扩展性和高可用性
弱点:
- 数据库事务一致性需求
- 数据库写实时性和读实时性需求
- 对于复杂的sql查询,特别是多表关联查询需求
5.2 MongoDB特点
- MongoDB是一个面向文档存储的数据库,操作起来简单便捷。
- 你可以在MongoDB记录中设置任何属性的索引(如:FirstName=“Sameer”,Address=“8 Gandi Road”)来实现更快的排序。
- 你可以通过本地或者网络创建数据镜像,这使得MongoDB有更强的扩展性。
- 如果负载的增加(需要更多的存储空间和更强的处理能力),它可以分布在计算机网络中其他节点上
- MongoDB支持丰富的查询表达式,查询指令使用JSON形式的标记,可轻易查询数据库中的内容
- 等等
5.3 SpringBoot集成MongoDB
5.3.1 集成简介
spring-data-mongodb,提供了MongoTemplate与 MongoRepository两种方式访问mongadb ,MongoRepository,操作简单,MongoTemplate,操作灵活,我们在项目中可以灵活适用这两种方式操作mongodb,MongoRepository的缺点是不够灵活,MongoTemplate,正好可以弥补不足。
5.3.2 搭建开发环境
- 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
- 添加配置
spring.data.mongodb.uri=mongodb://localhost:27017/test
test为数据库名字,localhost为服务器,27017为端口号
- 创建对应实体类
@Data
@Document("User")
public class User {
@Id
public String id;
public String name;
public Integer age;
public String email;
public String creatDate;
}
@Data为lombok组件,为实体类添加set和get方法;
@Document注解是用来指定数据库的collection,这样就知道该去操作哪个collection了;
@Id为我们自动生成mongodb数据库中 _Id 。
5.4 mongoTemplate
5.4.1 新增
在实体类中set好相应的字段,直接调用save方法,调用完成后,user中的id字段会被自动填值,可以取得主键。
@SpringBootTest
class MongoApplication(){
@Autowired
private MongoTemplate mongoTemplate;
//新增
public void insertUser(User user) {
mongoTemplate.insert(user);
}
}
5.4.2 查询
- 条件查询
先new一个Query,在构造方法中可以传一个Criteria,作为类似mysql的where语句,可以在后面用.and继续连接。
public void findUserList(){
Query query = new Query(Criteria.where("name").is("tjj")).and("age").is("18");
List<User> users = mongoTemplate.find(query, User.class);
}
- 根据Id查询
其中xxxx为数据库中的id值
public void findId(){
mongoTemplate.findById("xxxx",User.class)
}
- 模糊查询
注意条件的创建过程
public void findLikeUserList(){
String name = "tjj";
String regex = String.format("%s%s%s","^.*",".*$");
Pattern pattern = Pattern.compile(regex,Pattern.CASE_INSENSITIVE);
Query query = new Query(Criteria.where("name").regex(pattern);
List<User> users = mongoTemplate.find(query, User.class);
}
查询可以使用排序和分页
query.with(Sort.by(Sort.Order.desc("name")));
query.skip(0);
query.limit(1);
- 分页查询
public void findPageUserList(){
//当前页
int pageNo = 1;
//每页的记录数
int pageSize = 3;
String name = "tjj";
String regex = String.format("%s%s%s","^.*",".*$");
Pattern pattern = Pattern.compile(regex,Pattern.CASE_INSENSITIVE);
Query query = new Query(Criteria.where("name").regex(pattern);
List<User> users = mongoTemplate.find(query, User.class);
//查询记录数
long count = mongoTemplate.count(query,User.class);
List<User> users = mongoTemplate.find(query,skip((pageNo-1)*pageSize).limit(pageSize),User.class);
}
5.4.3 修改
public void updateUser(User user) {
//根据id查询
User user = mongoTemplate.findById("xxxx",User.class);
//设置修改值
user.setName("test_1");
user.setAge(20);
//调用方法实现修改
Query query = new Query(Criteria.where("_id").is(user.getId()));
Update update = new Update();
update.set("name",user.getName());
update.set("age",user.getAge());
UpdateResult upsert = mongoTemplate.upsert(query,update,User.class);
//获取修改的行数
long modifidedCount = upsert.getModifiedCount();
}
5.4.4 删除
public void deleteUser(String id) {
Query query = new Query(Criteria.where("_id").is("xxxx"));
//返回的是删除的行数
DeleteResult result = mongoTemplate.remove(query, User.class);
}
下面重点介绍一下复杂查询,会用到Aggregate聚合查询。
假设这样一个场景,MongoDB中存的json字符串含有一个数组,如
{
"name":"小明",
"no":"20180105",
"test":[
{"subject":"数学",
"result":98
},{"subject":"语文",
"result":80
},{"subject":"英语",
"result":91
}
]
},
{
"name":"小丽",
"no":"20180106",
"test":[
{"subject":"数学",
"result":92
},{"subject":"语文",
"result":95
},{"subject":"英语",
"result":93
}
]
}
这里存放的是学生的考试信息,包含姓名、学号和考试成绩,其中考试成绩是一个对象数组,包含了不同学科的考试成绩。
如果想只查询所有学生的数学成绩,用普通的查询方法,会把其他成绩也带出来,如果把数组拆开呢,这里就要使用聚合查询加上$unwind关键字。
Criteria condition = Criteria.where("test.subject").is("数学");
Aggregation aggregationQuery = Aggregation.newAggregation(Aggregation.unwind("test"),
Aggregation.match(condition),
Aggregation.sort(Sort.by(Sort.Order.desc("test.result"))),
Aggregation.skip((pageNo.longValue()-1L)*pageSize.longValue()),
Aggregation.limit(pageSize.longValue()));
@SuppressWarnings("rawtypes")
List<Map> list = mongoTemplate.aggregate(aggregation, Test.class,Map.class).getMappedResults();
这样就可以查到每个学生的数学成绩,按照成绩倒序排序,并且使用分页。注意,这里查出来的是个Map,并不是实体类。
关于其他的关键字,网上搜索有一大堆,这里就不赘述了。
如果result成绩是用String来存储的,这时候排序字符串可能就会有问题,那该怎么解决呢
aggregationQuery = aggregationQuery.withOptions(
new AggregationOptions.Builder()
.collation(Collation.of(new Locale("zh")).
numericOrderingEnabled()).build());
5.5 mongoRepository
- 创建一个interface去继承MongoRepository
@Repository
public interface UserRepository extends MongonRepository<User,String>{
}
- 将UserRepository注入
class MongoApplicationTest{
@Autowired
private UserRepository userRepository;
}
5.5.1 添加
public void creat(){
User user = new User();
user.setName("tjj");
user.setAge(18);
userRepository.save(user);
}
5.5.2 查询
public void findId(){
userRepository.findById("xxxx").get();
}
条件查询
public void findUserList(){
User user = new User();
user.setAge(18);
user。setName("tjj");
Example<User> userExample = Example.of(user);
userRepository.findAll(userExample)
}
模糊查询
比条件查询多一个匹配规则
public void findLikeUserList(){
//设置模糊查询匹配规则
ExampleMatcher matcher = ExampleMatcher.matching()
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)
.withIgnoreCase(true);
User user = new User();
user.setAge(18);
user.setName("t");
Example<User> userExample = Example.of(user,);
userRepository.findAll(userExample)
}
分页查询
public void findPageUserList(){
//分页参数设置
//0代表第一页
Pageable pageable = PageRequest.of(0,3);
User user = new User();
user。setName("tjj");
Example<User> userExample = Example.of(user);
Page<User> pages = userRepository.findAll(userExample,pageable)
}
5.5.3 修改
修改和添加都是调用的save,自动判断是否有Id,有id就修改,无id就添加
public void updateUser(){
User user = userRepository.findById("xxxx").get();
user.setAge(100);
User save = userRepository.save(user);
}