企业级博客项目笔记(二)

企业级博客项目笔记(二)

一、数据持久化

1.JPA简介

  • JPA(Java Persistence API)是用于管理Java EE 和Java SE 环境中的持久化,以及对象/关系映射的Java API
  • 封装JPA的框架有:EclipseLink、Hibernate、Apache OpenJPA
  • 注意:实体实例被当做值以分离对象的方式进行传递(例如通过会话bean的远程业务接口),则该类必须实现Serializable接口---序列化接口
  • 关系:
    • 一对一:@OneToOne
    • 一对多:@OneToMany
    • 多对一:@ManyToOne
    • 多对多:@ManyToMany

2.EntityManager

  • EntityManager接口
    • 定义用于与持久性上下文进行交互的方法
    • 创建和删除持久实体实例,通过实体的主键查找实体
    • 允许在实体上运行查询
  • 获取EntityManager实例:
@PersistenceUnit
EntityManagerFactory emf;
EntityManager em;
@Resource
UserTransaction utx;
...
em=emf.createEntityManager();
try{
   utx.begin();
   em.persist(SomeEntity);
   em.merge(AnotherEntity);
   em.remove(ThirdEntity);
   utx.commit();
}catch(Exception e){
   utx.rollback();
}
  • 查找实体
@PersistenceContext
EntityManager em;
public void enterOrder(int custID,CustomerOrder newOrder){
   Customer cust=em.find(Customer.class,custID);
   cust.getOrders().add(newOrder);
   newOrder.setCustomer(cust);
}

3.Spring Data JPA

  • Spring Data JPA简介
    • 是更大的Spring Data家族的一部分
    • 对基于JPA的数据访问层的增强支持
    • 更容易构建基于使用Spring数据访问技术栈的应用程序
  • CrudRepository接口:声明增删改查的方法
  • PagingAndSortingRepository接口:声明分页和排序的方法
  • Spring Data JPA 自定义接口(继承Repository接口)
    • 根据方法名创建查询
public interface PersonRepository extends Repository<User,Long>{
    List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress,String lastname);
    ...
}

二、Spring Data JPA、Hibernate与Spring Boot集成

1.配置环境

  • MySQL Community Server 5.7.17
  • Spring Data JPA 1.11.1.RELEASE
  • Hibernate 5.2.8.Final
  • MySQL Connector/J 6.0.5

2.修改build.gradle

//依赖关系
dependencies{
    ...
    //添加Spring Data JPA 的依赖
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    //添加MySQL连接驱动的依赖
    compile('mysql:mysql-connector-java:6.0.5')
    //添加H2数据库依赖
    runtime('com.h2database:h2:1.4.193')
    ...
} 
buildscript{
    ...
    //自定义Hibernate的版本
    ext['hibernate.version'] = '5.2.8.Final'
    ...
}

3.验证集成是否成功

  • 打开cmd,运行gradlew bootRun
  • 打开浏览器访问项目,创建用户成功,说明集成成功

三、数据持久化实战

1.后台编码

  • User.java
@Entity //实体
public class User {
    @Id  //主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) //主键生成策略:自增
    private Long id; //实体的唯一标识
    private String name; //用户名称
    private String email; //用户邮箱

    protected User(){}   //无参构造函数,设为protected防止直接使用
    public User(Long id,String name,String email){
        this.id=id;
        this.name=name;
        this.email=email;
    }
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
    @Override   //重写toString方法
    public String toString(){
        return String.format("User[id=%d,name='%s',email='%s']",id,name,email);
    }
}
  • UserRepository.java
public interface UserRepository extends CrudRepository<User,Long> {

}
  • UserController.java
@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    /**
     * 获取用户列表
     * @param model
     * @return
     */
    @GetMapping
    public ModelAndView list(Model model){
        model.addAttribute("userList",userRepository.findAll());
        model.addAttribute("title","用户管理");
        return new ModelAndView("users/list","userModel",model);
    }

    /**
     * 根据id查询用户
     * @param id
     * @param model
     * @return
     */
    @GetMapping("{id}")
    public ModelAndView view(@PathVariable("id")Long id, Model model){
        User user=userRepository.findOne(id);
        model.addAttribute("user",user);
        model.addAttribute("title","查看用户");
        return new ModelAndView("users/view","userModel",model);
    }

    /**
     * 获取创建表单页面
     * @param model
     * @return
     */
    @GetMapping("/form")
    public ModelAndView createForm(Model model){
        model.addAttribute("user",new User(null,null,null));
        model.addAttribute("title","创建用户");
        return new ModelAndView("users/form","userModel",model);
    }

    /**
     * 更新用户
     * @param user
     * @return
     */
    @PostMapping
    public ModelAndView saveOrUpdateUser(User user){
        user=userRepository.save(user);
        return new ModelAndView("redirect:/users");
    }

    /**
     * 根据id删除用户
     * @param id
     * @return
     */
    @GetMapping("/delete/{id}")
    public ModelAndView delete(@PathVariable("id")Long id){
        userRepository.delete(id);
        return new ModelAndView("redirect:/users");
    }

    /**
     * 根据id修改用户
     * @param id
     * @param model
     * @return
     */
    @GetMapping("/modify/{id}")
    public ModelAndView modify(@PathVariable("id")Long id,Model model){
        User user=userRepository.findOne(id);
        model.addAttribute("user",user);
        model.addAttribute("title","修改用户");
        return new ModelAndView("users/form","userModel",model);
    }

}

2.查看H2数据库

  • 修改application.properties 
    #使用H2控制台 
    spring.h2.console.enable=true
  • 浏览器访问:localhost:8080/h2-console
  • JDBC URL:jdbc:h2:mem:testdb

四、数据持久化到MySQL数据库

1. 修改application.properties

#DataSource
spring.datasource.url=jdbc:mysql://localhost/blog?useSSL=false&serverTimezone=UTC&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#JPA
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop

2.运行项目

  • 启动MySQL Server
  • 创建blog数据库
  • 运行项目

五、全文搜索

1.全文搜索简介

  • 数据结构
    • 结构化:指具有固定格式或有限长度的数据,如数据库,元数据等。
    • 非结构化:指不定长或无固定格式的数据,如邮件,word文档等。
  • 非结构化数据的检索
    • 顺序扫描法(Serial Scanning)
    • 全文搜索(Full-text Search):将非结构化的数据转为结构化的数据。
  • 全文搜索实现原理
    • 建立文本库
    • 建立索引
    • 执行搜索
    • 过滤结果
  • 基于Java的开源实现
    • Lucene:开源全文搜索引擎
    • ElasticSearch:基于Lucene的全文检索框架,实时搜索效率高
    • Solr:类似ElasticSearch,基于zookeeper的分布式管理

2.ElasticSearch简介

  • 高度可扩展的开源全文搜索和分析引擎
  • 快速地、近实时地对大数据进行存储、搜索和分析
  • 用来支撑有复杂的数据搜索需求的企业级应用

3.ElasticSearch的特点

  • 分布式
  • 高可用
  • 多类型
  • 多API
  • 面向文档
  • 异步写入
  • 近实时
  • 基于Lucene
  • Apache协议

4.ElasticSearch核心概念

  • 近实时:每隔n秒进行刷新,新创建的索引保存在文件系统中,在刷新的时候被存入。
  • 集群:一个或多个节点的集合,保存应用的所有数据。每一个集群都有一个名称。
  • 节点:一个单台服务器,用来保存数据,并参与整个集群的索引和操作。
  • 索引:用来加快搜索速度、是所有文档的集合。
  • 类型:根据文档的属性来划分类型。
  • 文档:进行索引的基本单位,使用json格式表示。
  • 分片:将索引分配到不同的节点中。
  • 副本:为了防止数据丢失,增加吞吐量。

5.Elasticsearch与Spring Boot集成

  • 配置环境
    • Elasticsearch 2.4.4
    • Spring Data Elasticsearch 2.1.3.RELEASE
    • JNA 4.3.9 --->访问操作系统原生应用
  • 修改build.gradle
//依赖关系
dependencies{
  ...
  //添加 Spring Data ElasticSearch的依赖
  compile('org.springframework.boot:spring-boot-starter-data-elasticsearch')
   //添加JNA的依赖
  compile('net.java.dev.jna:jna:4.3.0')
  ...
}

6.Elasticsearch实战

  • 修改application.properties
# Elasticsearch服务地址
spring.data.elasticsearch.cluster-nodes=localhost:9300
# 设置连接超时时间
spring.data.elasticsearch.properties.transport.tcp.connect_timeout=120s
  • 后台编码:

    • 文档 EsBlog
    • 资源库EsBlogRepository
    • 资源库测试用例 EsBlogRepositoryTest
    • 控制器 BlogController
  • EsBlog

    package com.zhuyong.domain.es;

    import org.springframework.data.elasticsearch.annotations.Document;

    import javax.persistence.Id;
    import java.io.Serializable;
    /**
    * @Author: zy
    * @Description: 
    * @Date: 2018/6/19_14:19
    **/

    @Document(indexName = "blog",type = "blog")   //文档
    public class EsBlog implements Serializable{

        private static final long serialVersionUID=1L;

        @Id   //主键
        private String id;
        private String title;
        private String summary; //摘要
        private String content; //正文内容

        protected EsBlog(){   //JPA 规范要求,防止直接使用
        }
        public EsBlog(String title,String summary,String content){
            this.title=title;
            this.summary=summary;
            this.content=content;
        }
        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getSummary() {
            return summary;
        }

        public void setSummary(String summary) {
            this.summary = summary;
        }

        public String getContent() {
            return content;
        }

        public void setContent(String content) {
            this.content = content;
        }
        @Override
        public String toString(){
            return String.format("EsBlog[id='%s',title='%s',summary='%s'],content='%s'",id,title,summary,content);
        }
    }
  • EsBlogRepository
    package com.zhuyong.repository.es;

    import com.zhuyong.domain.es.EsBlog;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

    /**
    * @Author: zy
    * @Description: EsBlog Repository 接口
    * @Date: 2018/6/19_13:48
    **/

    public interface EsBlogRepository extends ElasticsearchRepository<EsBlog,String> {
         /**
         * @Author: zy
         * @Description: 分页查询博客(去重)
         * @Date: 2018/6/20_16:26
         **/
         Page<EsBlog>  findDistinctEsBlogByTitleContainingOrSummaryContainingOrContentContaining(String title, String summary, String content, Pageable pageable);
    }
  • EsBlogRepositoryTest
    package com.zhuyong.repository.es;

    import com.zhuyong.domain.es.EsBlog;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Pageable;
    import org.springframework.test.context.junit4.SpringRunner;

    import static org.assertj.core.api.Assertions.assertThat;


    /**
    * @Author: zy
    * @Description: EsBlogRepository接口测试
    * @Date: 2018/6/20_16:29
    **/

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class EsBlogRepositoryTest {

        @Autowired
        private EsBlogRepository esBlogRepository;

        @Before
        public void initRepositoryData(){
            //清除所有数据
           esBlogRepository.deleteAll();

           esBlogRepository.save(new EsBlog("登鹳雀楼","王之涣的登鹳雀楼",
                           "白日依山尽,黄河入海流。欲穷千里目,更上一层楼。"));
           esBlogRepository.save(new EsBlog("相思","王维的相思",
                    "红豆生南国,春来发几枝。愿君多采撷,此物最相思。"));
           esBlogRepository.save(new EsBlog("静夜思","李白的静夜思",
                    "床前明月光,疑是地上霜。举头望明月,低头思故乡。"));
        }
        @Test
        public void findDistinctEsBlogByTitleContainingOrSummaryContainingOrContentContaining() throws Exception {
            Pageable pageable=new PageRequest(0,20);
            String title="思";
            String summary="相思";
            String content="相思";
            Page<EsBlog> page=esBlogRepository.findDistinctEsBlogByTitleContainingOrSummaryContainingOrContentContaining(title,summary,content,pageable);
            assertThat(page.getTotalElements()).isEqualTo(2);
            System.out.println("----------------start 1");
            for(EsBlog blog:page.getContent()){
                System.out.println(blog.toString());
            }
            System.out.println("----------------end 1");
        }

    }
  • BlogController
    package com.zhuyong.controller;

    import com.zhuyong.domain.es.EsBlog;
    import com.zhuyong.repository.es.EsBlogRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Pageable;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.List;

    /**
    * @Author: zy
    * @Description: Blog 控制器
    * @Date: 2018/6/20_17:02
    **/

    @RestController
    @RequestMapping("/blogs")
    public class BlogController {

        @Autowired
        private EsBlogRepository esBlogRepository;

        @GetMapping()
        public List<EsBlog> list(@RequestParam(value="title") String title,
                                 @RequestParam(value="summary") String summary,
                                 @RequestParam(value="content") String content,
                                 @RequestParam(value="pageIndex",defaultValue= "0") int pageIndex,
                                 @RequestParam(value="pageSize",defaultValue = "10") int pageSize){
            //数据在Test中初始化
            Pageable pageable=new PageRequest(pageIndex,pageSize);
            Page<EsBlog> page=esBlogRepository.findDistinctEsBlogByTitleContainingOrSummaryContainingOrContentContaining(title,summary,content,pageable);
           return page.getContent();
        }
    }

猜你喜欢

转载自blog.csdn.net/qq_31247177/article/details/80738348