SpringBoot学习过程

最近这两年最流行的java框架也属SpringBoot了,早在前几年我一直用NinjaFramwork这个java框架,也是很是优秀,不过最近在面试各家公司的过程当中最为流行的仍是SpringBoot了,所以也学一下吧javascript

 

https://spring.io/projects/spring-boot#samples 这是官方教程,秒杀一切教程css

 

https://github.com/spring-projects/spring-boot 这是官方教程,秒杀一切教程html

 

以上是官方demo,建议学习就学官方的,接下来我把学习过程以及整个技术选型记录下来,若有建议或更正但愿你们能提出.前端

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------java

 1:为何使用SpringBoot而不使用Ninjaframwork?mysql

没有为何,你们都在用而已,Ninjaframwork也很是好,官网Ninja - full stack web framework for Java -react

Spring Boot的核心思想就是约定大于配置,一切自动完成。采用Spring Boot能够大大的简化你的开发模式,全部你想集成的经常使用框架,它都有对应的组件支持linux

而我用了接近4年的Ninjaframwork也是如此优秀,本着学习的原则选择一下springbootgit

 

2:初步了解一下springboot吧。(官方已经很详细的介绍,但仍是想看一下国内一些企业或我的的想法)github

参考http://www.最代码(改成英文便可).com/blog/3545313038879744.htm    (博客园居然禁止)

http://www.ityouknow.com/springboot/2016/01/06/springboot(%E4%B8%80)-%E5%85%A5%E9%97%A8%E7%AF%87.html

java -jar app.jar --spring.profiles.active=dev 启动测试环境的配置文件

 

3:学习构建一个SpringBoot,使用maven仍是gradle构建项目呢?

用了接近4年的maven 本着学习的原则,直接换gradle  ,官方介绍参考https://gradle.org/ 

国内人士分析参考https://blog.csdn.net/zguoshuaiiii/article/details/78376331

 

4:Spring Data JPA  hibernate   什么关系?

参考http://www.cnblogs.com/xiaoheike/p/5150553.html

https://blog.csdn.net/linux__xu/article/details/75574342

https://blog.csdn.net/xihuanyuye/article/details/81201441

https://blog.csdn.net/qq897958555/article/details/53208002

 

 

 

5 项目使用什么数据库链接呢? 不用想太多,直接postgresql 便可

6 接下来 就要登录系统,那么 Shiro和Spring Security对比  ,参考:原文:https://blog.csdn.net/liyuejin/article/details/77838868

Shiro简介

Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人愈来愈多,由于它至关简单,对比Spring Security,可能没有Spring Security作的功能强大,可是在实际工做时可能并不须要那么复杂的东西,因此使用小而简单的Shiro就足够了。对于它俩到底哪一个好,这个没必要纠结,能更简单的解决项目问题就行了。

Shiro架构与功能介绍

1.认证与受权相关基本概念

两个基本的概念

安全实体:系统须要保护的具体对象数据

权限:系统相关的功能操做,例如基本的CRUD

Authentication:身份认证/登陆,验证用户是否是拥有相应的身份;

Authorization:受权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能作事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具备某个权限;

Session Manager:会话管理,即用户登陆后就是一次会话,在没有退出以前,它的全部信息都在会话中;会话能够是普通JavaSE环境的,也能够是如Web环境的;

Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

Web Support:Web支持,能够很是容易的集成到Web环境;

Caching:缓存,好比用户登陆后,其用户信息、拥有的角色/权限没必要每次去查,这样能够提升效率;

Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另外一个线程,能把权限自动传播过去;

Testing:提供测试支持;

Run As:容许一个用户伪装为另外一个用户(若是他们容许)的身份进行访问;

Remember Me:记住我,这个是很是常见的功能,即一次登陆后,下次再来的话不用登陆了。

2.Shiro四大核心功能:Authentication,Authorization,Cryptography,Session Management
Java安全框架Shiro和Spring Security对比

Shiro架构

3.Shiro三个核心组件:Subject, SecurityManager 和 Realms.

Subject:主体,表明了当前“用户”,这个用户不必定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;全部Subject都绑定到SecurityManager,与Subject的全部交互都会委托给SecurityManager;能够把Subject认为是一个门面;SecurityManager才是实际的执行者;

SecurityManager:安全管理器;即全部与安全有关的操做都会与SecurityManager交互;且它管理着全部Subject;能够看出它是Shiro的核心,它负责与后边介绍的其余组件进行交互,若是学习过SpringMVC,你能够把它当作DispatcherServlet前端控制器;

Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它须要从Realm获取相应的用户进行比较以肯定用户身份是否合法;也须要从Realm获得用户相应的角色/权限进行验证用户是否能进行操做;能够把Realm当作DataSource,即安全数据源。

 

Spring Security简介

Spring Security是一个可以为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组能够在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减小了为企业系统安全控制编写大量重复代码的工做。它是一个轻量级的安全框架,它确保基于Spring的应用程序提供身份验证和受权支持。它与Spring MVC有很好地集成,并配备了流行的安全算法实现捆绑在一块儿。安全主要包括两个操做“认证”与“验证”(有时候也会叫作权限控制)。“认证”是为用户创建一个其声明的角色的过程,这个角色能够一个用户、一个设备或者一个系统。“验证”指的是一个用户在你的应用中可以执行某个操做。在到达受权判断以前,角色已经在身份认证过程当中创建了。

它的设计是基于框架内大范围的依赖的,能够被划分为如下几块。

    Web/Http 安全:这是最复杂的部分。经过创建 filter 和相关的 service bean 来实现框架的认证机制。当访问受保护的 URL 时会将用户引入登陆界面或者是错误提示界面。

    业务对象或者方法的安全:控制方法访问权限的。

    AuthenticationManager:处理来自于框架其余部分的认证请求。

    AccessDecisionManager:为 Web 或方法的安全提供访问决策。会注册一个默认的,可是咱们也能够经过普通 bean 注册的方式使用自定义的 AccessDecisionManager。

    AuthenticationProvider:AuthenticationManager 是经过它来认证用户的。

    UserDetailsService:跟 AuthenticationProvider 关系密切,用来获取用户信息的。


Java安全框架Shiro和Spring Security对比


Shiro和Spring Security比较

    Shiro比Spring更容易使用,实现和最重要的理解

    Spring Security更加知名的惟一缘由是由于品牌名称

    “Spring”以简单而闻名,但讽刺的是不少人发现安装Spring Security很难

    然而,Spring Security却有更好的社区支持

    Apache Shiro在Spring Security处理密码学方面有一个额外的模块

    Spring-security 对spring 结合较好,若是项目用的springmvc ,使用起来很方便。可是若是项目中没有用到spring,那就不要考虑它了。

    Shiro 功能强大、且 简单、灵活。是Apache 下的项目比较可靠,且不跟任何的框架或者容器绑定,能够独立运行
---------------------

初步理解Spring Security并实践 参考:https://www.jianshu.com/p/e6655328b211

 

pring Security主要作两件事,一件是认证,一件是受权。

1.Spring Security初体验

 

Spring Security如何使用,先在你的项目pom.xml文件中声明依赖。

<dependency>
    <!-- 因为我使用的spring boot因此我是引入spring-boot-starter-security并且我使用了spring io因此不须要填写依赖的版本号 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> 

而后建立一个类并继承WebSecurityConfigurerAdapter这个方法,并在之类中重写configure的3个方法,其中3个方法中参数包括为HttpSecurity(HTTP请求安全处理),AuthenticationManagerBuilder(身份验证管理生成器)和WebSecurity(WEB安全)。

public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth){ super.configure(auth); } @Override protected void configure(HttpSecurity http){ super.configure(http); } @Override protected void configure(WebSecurity web){ super.configure(web); } } 

接下来咱们先看看protected void configure(HttpSecurity http)这个方法提供了一个默认的配置。

protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .httpBasic(); } 

http.authorizeRequests()其中这里的意思是指经过authorizeRequests()方法来开始请求权限配置。
而接着的.anyRequest().authenticated()是对http全部的请求必须经过受权认证才能够访问。

直观描述
方法描述

而and()是返回一个securityBuilder对象,formLogin()和httpBasic()是受权的两种方式。


httpBasic()受权认证
formLogin()受权认证

固然这些界面都是spring security原生的界面,咱们也能够自定义咱们的formLogin页面!

protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() //指定登陆页的路径 .loginPage("/login") //必须容许全部用户访问咱们的登陆页(例如未验证的用户,不然验证流程就会进入死循环) //这个formLogin().permitAll()方法容许全部用户基于表单登陆访问/login这个page。 .permitAll(); } 

提示一下,这个自定义表单登陆的自定义页面中的登陆名参数必须被命名为username
密码参数必须被命名为password。
而接下来当咱们须要对某些开放的url,给与任何人访问的时候,咱们应该如何设置呢?答案很简单咱们先看着代码慢慢深刻!

protected void configure(HttpSecurity http) throws Exception { http //http.authorizeRequests()方法有多个子节点,每一个macher按照他们的声明顺序执行 .authorizeRequests() //咱们指定任何用户均可以访问多个URL的模式。 //任何用户均可以访问以"/resources/","/signup", 或者 "/about"开头的URL。 .antMatchers("/resources/**", "/signup", "/about").permitAll() //以 "/admin/" 开头的URL只能让拥有 "ROLE_ADMIN"角色的用户访问。 //请注意咱们使用 hasRole 方法,没有使用 "ROLE_" 前缀。 .antMatchers("/admin/**").hasRole("ADMIN") //任何以"/db/" 开头的URL须要同时具备 "ROLE_ADMIN" 和 "ROLE_DBA"权限的用户才能够访问。 //和上面同样咱们的 hasRole 方法也没有使用 "ROLE_" 前缀。 .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") //任何以"/db/" 开头的URL只须要拥有 "ROLE_ADMIN" 和 "ROLE_DBA"其中一个权限的用户才能够访问。 //和上面同样咱们的 hasRole 方法也没有使用 "ROLE_" 前缀。 .antMatchers("/db/**").hasAnyRole("ADMIN", "DBA") //还没有匹配的任何URL都要求用户进行身份验证 .anyRequest().authenticated() .and() // ... .formLogin(); } 

咱们能够在authorizeRequests() 后定义多个antMatchers()配置器来控制不一样的url接受不一样权限的用户访问,而其中permitAll() 方法是运行全部权限用户包含匿名用户访问。
而hasRole("权限")则是容许这个url给与参数中相等的权限访问。
access("hasRole('权限') and hasRole('权限')") 是指容许访问这个url必须同时拥有参数中多个身份权限才能够访问。
hasAnyRole("ADMIN", "DBA")是指容许访问这个url必须同时拥有参数中多个身份权限中的一个就能够访问该url。


舒适提示!这里就是为何在hasRole和hasAnyRole中不须要加"ROLE_" 前缀的因由!

2.Spring Security定制登陆退出行为

咱们接下来就简单的定制一下登陆登出行为!

protected void configure(HttpSecurity http) throws Exception { http //经过formlogin方法登陆,并设置登陆url为/api/user/login .formLogin().loginPage("/api/user/login") //指定登陆成功后跳转到/index页面 .defaultSuccessUrl("/index") //指定登陆失败后跳转到/login?error页面 .failureUrl("/login?error") .permitAll() .and() //开启cookie储存用户信息,并设置有效期为14天,指定cookie中的密钥 .rememberMe().tokenValiditySeconds(1209600).key("mykey") .and() .logout() //指定登出的url .logoutUrl("/api/user/logout") //指定登场成功以后跳转的url .logoutSuccessUrl("/index") .permitAll(); } 

3.Spring Security定制自定义用户认证

用户认证流程
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override //重写了configure参数为AuthenticationManagerBuilder的方法 protected void configure(AuthenticationManagerBuilder auth){ //并根据传入的AuthenticationManagerBuilder中的userDetailsService方法来接收咱们自定义的认证方法。 //且该方法必需要实现UserDetailsService这个接口。 auth.userDetailsService(new myUserDetailsService()) //密码使用BCryptPasswordEncoder()方法验证,由于这里使用了BCryptPasswordEncoder()方法验证。因此在注册用户的时候在接收前台明文密码以后也须要使用BCryptPasswordEncoder().encode(明文密码)方法加密密码。 .passwordEncoder(new BCryptPasswordEncoder());; } @Override protected void configure(HttpSecurity http){ super.configure(http); } @Override protected void configure(WebSecurity web){ super.configure(web); } } 

新建myUserDetailsService方法并实现UserDetailsService这个接口

@Component public class myUserDetailsService implements UserDetailsService { @Autowired //因为是演示这里就再也不建立service层了,直接注入UserRepository。 private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { //查询帐号是否存在,是就返回一个UserDetails的对象,否就抛出异常! User user = userRepository.findByName(userName); if (user == null) { throw new UsernameNotFoundException("UserName " + userName + " not found"); } return new SecurityUser(user); } } 

基本的认证逻辑就到这里了,对于有另外的业务需求均可以在自定义的myUserDetailsService中处理完成!

4.Spring Security定制自定义受权策略

@EnableGlobalAuthentication public class SecurityConfig extends WebSecurityConfigurerAdapter{ @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() .and() .csrf().disable() .authorizeRequests() .antMatchers("/admin").permitAll() //使用自定义受权策略 .anyRequest().access("@mySecurity.check(authentication,request)"); } } 

新建MySecurity类

@Component("mySecurity") public class MySecurity(){ //这里应该注入用户和该用户所拥有的权限(权限在登陆成功的时候已经缓存起来,当须要访问该用户的权限是,直接从缓存取出!),而后验证该请求是否有权限,有就返回true,不然则返回false不容许访问该Url。 //并且这里还传入了request,我也可使用request获取该次请求的类型。 //根据restful风格咱们可使用它来控制咱们的权限,例如当这个请求是post请求,证实该请求是向服务器发送一个新建资源请求,咱们可使用request.getMethod()来获取该请求的方式,而后在配合角色所容许的权限路径进行判断和受权操做! public boolean check(Authentication authentication, HttpServletRequest request){ //若是能获取到Principal对象不为空证实,受权已经经过 Object principal = authentication.getPrincipal(); if(principal != null && principal instanceof UserDetails){ //获取请求登陆的url System.out.println(((UserDetails)principal).getAuthorities()) ; return true; } return false; } }

 -----------------------------------------------------------------------------------------------------------------------

Spring Boot中CrudRepository与JpaRepository

参考https://blog.csdn.net/xuemengrui12/article/details/80525227

先看下二者的接口代码:

    @NoRepositoryBean
    public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
        <S extends T> S save(S var1);
        <S extends T> Iterable<S> save(Iterable<S> var1);
        T findOne(ID var1);
        boolean exists(ID var1);
        Iterable<T> findAll();
        Iterable<T> findAll(Iterable<ID> var1);
        long count();
        void delete(ID var1);
        void delete(T var1);
        void delete(Iterable<? extends T> var1);
        void deleteAll();
    }

    @NoRepositoryBean
    public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
        List<T> findAll();
        List<T> findAll(Sort var1);
        List<T> findAll(Iterable<ID> var1);
        <S extends T> List<S> save(Iterable<S> var1);
        void flush();
        <S extends T> S saveAndFlush(S var1);
        void deleteInBatch(Iterable<T> var1);
        void deleteAllInBatch();
        T getOne(ID var1);
        <S extends T> List<S> findAll(Example<S> var1);
        <S extends T> List<S> findAll(Example<S> var1, Sort var2);
    }

看一下他们的继承关系


注意下二者的save方法的不一样,JpaRepository 中的save方法实现源码:

      @Transactional
        public <S extends T> List<S> save(Iterable<S> entities) {
            List<S> result = new ArrayList<S>();
            if (entities == null) {
                return result;
            }
            for (S entity : entities) {
                result.add(save(entity));
            }
            return result;
        }

CrudRepository 中的save方法源代码

    @Transactional
        public <S extends T> S save(S entity) {
            if (entityInformation.isNew(entity)) {
                em.persist(entity);//是新的就插入
                return entity;
            } else {
                return em.merge(entity); //不是新的merge
            }
        }

由源码可知CrudRepository 中的save方法是至关于merge+save ,它会先判断记录是否存在,若是存在则更新,不存在则插入记录。唉,仍是须要多看源码啊

参考:

https://blog.csdn.net/zgf19930504/article/details/50537222
https://blog.csdn.net/strive_peter/article/details/76276758
https://blog.csdn.net/hikeboy/article/details/58597053
http://makaidong.com/youhan26/1/976_10156939.html

到底用哪个 参考:https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-jpa/index.html

Spring Boot, MySQL, JPA, Hibernate Restful CRUD API Tutorial

参考https://www.callicoder.com/spring-boot-rest-api-tutorial-with-mysql-jpa-hibernate/

Spring Boot has taken Spring framework to the next level. It has drastically reduced the configuration and setup time required for spring projects.

You can setup a project with almost zero configuration and start building the things that actually matter to your application.

If you are new to Spring boot and want to get started with it quickly, then this blog post is for you.

In this post, we’ll build a Restful CRUD API for a simple note-taking application. A Note can have a title and some content. We’ll first build the apis to create, retrieve, update and delete a Note, and then test them using postman.

So, Let’s get started!

 

Creating the Project

Spring Boot provides a web tool called Spring Initializer to bootstrap an application quickly. Just go to http://start.spring.io and follow the steps below to generate a new project.

Step 1 : Click Switch to full version on http://start.spring.io page.

Step 2 : Enter the details as follows -

  • Group : com.example
  • Artifact : easy-notes
  • Name : easy-notes
  • Description : Rest API for a Simple Note Taking Application
  • Package Name : com.example.easynotes
  • Packaging : jar (This is the default value)
  • Java Version : 1.8 (Default)
  • Dependencies : Web, JPA, MySQL, DevTools

Spring Boot Rest API Service Example

Once all the details are entered, click Generate Project to generate and download your project. Spring Initializer will generate the project with the details you have entered and download a zip file with all the project folders.

Next, Unzip the downloaded zip file and import it into your favorite IDE.

Exploring the Directory Structure

Following is the directory structure of our Note taking application -

Spring Boot Mysql JPA Hibernate Rest API Tutorial Directory Structure

Let’s understand the details of some of the important files and directories -

1. EasyNotesApplication

This is the main entry point of our Spring Boot application.

package com.example.easynotes; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class EasyNotesApplication { public static void main(String[] args) { SpringApplication.run(EasyNotesApplication.class, args); } } 

It contains a simple annotation called @SpringBootApplication which is a combination of the following more specific spring annotations -

  • @Configuration : Any class annotated with @Configuration annotation is bootstrapped by Spring and is also considered as a source of other bean definitions.

  • @EnableAutoConfiguration : This annotation tells Spring to automatically configure your application based on the dependencies that you have added in the pom.xml file.

    For example, If spring-data-jpa or spring-jdbc is in the classpath, then it automatically tries to configure a DataSource by reading the database properties from application.properties file.

  • @ComponentScan : It tells Spring to scan and bootstrap other components defined in the current package (com.example.easynotes) and all the sub-packages.

The main() method calls Spring Boot’s SpringApplication.run() method to launch the application.

2. resources/

This directory, as the name suggests, is dedicated to all the static resources, templates and property files.

  • resources/static - contains static resources such as css, js and images.

  • resources/templates - contains server-side templates which are rendered by Spring.

  • resources/application.properties - This file is very important. It contains application-wide properties. Spring reads the properties defined in this file to configure your application. You can define server’s default port, server’s context path, database URLs etc, in this file.

    You can refer this page for common application properties used in Spring Boot.

3. EasyNotesApplicationTests - Define unit and integration tests here.

4. pom.xml - contains all the project dependencies

Configuring MySQL Database

As I pointed out earlier, Spring Boot tries to auto-configure a DataSource if spring-data-jpa is in the classpath by reading the database configuration from application.properties file.

So, we just have to add the configuration and Spring Boot will take care of the rest.

Open application.properties file and add the following properties to it.

## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties) spring.datasource.url = jdbc:mysql://localhost:3306/notes_app?useSSL=false spring.datasource.username = root spring.datasource.password = root ## Hibernate Properties # The SQL dialect makes Hibernate generate better SQL for the chosen database spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect # Hibernate ddl auto (create, create-drop, validate, update) spring.jpa.hibernate.ddl-auto = update 

You will need to create a database named notes_app in MySQL, and change the spring.datasource.username & spring.datasource.password properties as per your MySQL installation.

In the above properties file, the last two properties are for hibernate. Spring Boot uses Hibernate as the default JPA implementation.

The property spring.jpa.hibernate.ddl-auto is used for database initialization. I’ve used the value “update” for this property.

It does two things -

  • When you define a domain model, a table will automatically be created in the database and the fields of the domain model will be mapped to the corresponding columns in the table.

  • Any change to the domain model will also trigger an update to the table. For example, If you change the name or type of a field, or add another field to the model, then all these changes will be reflected in the mapped table as well.

Using update for spring.jpa.hibernate.ddl-auto property is fine for development. But, For production, You should keep the value of this property to “validate”, and use a database migration tool like Flyway for managing changes in the database schema.

Creating the Note model

All right! Let’s now create the Note model. Our Note model has following fields -

  • id: Primary Key with Auto Increment.
  • title: The title of the Note. (NOT NULL field)
  • content: Note’s content. (NOT NULL field)
  • createdAt: Time at which the Note was created.
  • updatedAt: Time at which the Note was updated.

Now, let’s see how we can model this in Spring. Create a new package called model inside com.example.easynotes and add a class named Note.java with following contents -

package com.example.easynotes.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.*; import javax.validation.constraints.NotBlank; import java.util.Date; @Entity @Table(name = "notes") @EntityListeners(AuditingEntityListener.class) @JsonIgnoreProperties(value = {"createdAt", "updatedAt"}, allowGetters = true) public class Note implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank private String title; @NotBlank private String content; @Column(nullable = false, updatable = false) @Temporal(TemporalType.TIMESTAMP) @CreatedDate private Date createdAt; @Column(nullable = false) @Temporal(TemporalType.TIMESTAMP) @LastModifiedDate private Date updatedAt; // Getters and Setters ... (Omitted for brevity) } 
  • All your domain models must be annotated with @Entity annotation. It is used to mark the class as a persistent Java class.

  • @Table annotation is used to provide the details of the table that this entity will be mapped to.

  • @Id annotation is used to define the primary key.

  • @GeneratedValue annotation is used to define the primary key generation strategy. In the above case, we have declared the primary key to be an Auto Increment field.

  • @NotBlank annotation is used to validate that the annotated field is not null or empty.

  • @Column annotation is used to define the properties of the column that will be mapped to the annotated field. You can define several properties like name, length, nullable, updateable etc.

    By default, a field named createdAt is mapped to a column named created_at in the database table. i.e. all camel cases are replaced with underscores.

    If you want to map the field to a different column, you can specify it using -

    @Column(name = "created_on") private String createdAt; 
  • @Temporal annotation is used with java.util.Date and java.util.Calendar classes. It converts the date and time values from Java Object to compatible database type and vice versa.

  • @JsonIgnoreProperties annotation is a Jackson annotation. Spring Boot uses Jackson for Serializing and Deserializing Java objects to and from JSON.

    This annotation is used because we don’t want the clients of the rest api to supply the createdAt and updatedAt values. If they supply these values then we’ll simply ignore them. However, we’ll include these values in the JSON response.

Enable JPA Auditing

In our Note model we have annotated createdAt and updatedAt fields with @CreatedDate and @LastModifiedDate annotations respectively.

Now, what we want is that these fields should automatically get populated whenever we create or update an entity.

To achieve this, we need to do two things -

1. Add Spring Data JPA’s AuditingEntityListener to the domain model.

We have already done this in our Note model with the annotation @EntityListeners(AuditingEntityListener.class).

2. Enable JPA Auditing in the main application.

Open EasyNotesApplication.java and add @EnableJpaAuditing annotation.

package com.example.easynotes; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication @EnableJpaAuditing public class EasyNotesApplication { public static void main(String[] args) { SpringApplication.run(EasyNotesApplication.class, args); } } 
 

Creating NoteRepository to access data from the database

The next thing we’re gonna do is create a repository to access Note’s data from the database.

Well, Spring Data JPA has got us covered here. It comes with a JpaRepository interface which defines methods for all the CRUD operations on the entity, and a default implementation of JpaRepository called SimpleJpaRepository.

Cool! Let’s create the repository now. First, Create a new package called repository inside the base package com.example.easynotes. Then, create an interface called NoteRepository and extend it from JpaRepository -

package com.example.easynotes.repository; import com.example.easynotes.model.Note; import org.springframework.data.jpa.repository.JpaRepository; @Repository public interface NoteRepository extends JpaRepository<Note, Long> { } 

Note that, we have annotated the interface with @Repository annotation. This tells Spring to bootstrap the repository during component scan.

Great! That is all you have to do in the repository layer. You will now be able to use JpaRepository’s methods like save(), findOne(), findAll(), count(), delete() etc.

You don’t need to implement these methods. They are already implemented by Spring Data JPA’s SimpleJpaRepository. This implementation is plugged in by Spring automatically at runtime.

Checkout all the methods available from SimpleJpaRepository’s documentation.

Spring Data JPA has a bunch of other interesting features like Query methods (dynamically creating queries based on method names), Criteria API, Specifications, QueryDsl etc.

I strongly recommend you to checkout the Spring Data JPA’s documentation to learn more.

Creating Custom Business Exception

We’ll define the Rest APIs for creating, retrieving, updating, and deleting a Note in the next section.

The APIs will throw a ResourceNotFoundException whenever a Note with a given id is not found in the database.

Following is the definition of ResourceNotFoundException. (I’ve created a package named exception inside com.example.easynotes to store this exception class) -

package com.example.easynotes.exception; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(value = HttpStatus.NOT_FOUND) public class ResourceNotFoundException extends RuntimeException { private String resourceName; private String fieldName; private Object fieldValue; public ResourceNotFoundException( String resourceName, String fieldName, Object fieldValue) { super(String.format("%s not found with %s : '%s'", resourceName, fieldName, fieldValue)); this.resourceName = resourceName; this.fieldName = fieldName; this.fieldValue = fieldValue; } public String getResourceName() { return resourceName; } public String getFieldName() { return fieldName; } public Object getFieldValue() { return fieldValue; } } 

Notice the use of @ResponseStatus annotation in the above exception class. This will cause Spring boot to respond with the specified HTTP status code whenever this exception is thrown from your controller.

Creating NoteController

The Final Step - We’ll now create the REST APIs for creating, retrieving, updating and deleting a Note.

First, create a new package controller inside com.example.easynotes. Then, create a new class NoteController.java with the following contents -

package com.example.easynotes.controller; import com.example.easynotes.exception.ResourceNotFoundException; import com.example.easynotes.model.Note; import com.example.easynotes.repository.NoteRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.List; @RestController @RequestMapping("/api") public class NoteController { @Autowired NoteRepository noteRepository; // Get All Notes // Create a new Note // Get a Single Note // Update a Note // Delete a Note } 

@RestController annotation is a combination of Spring’s @Controller and @ResponseBody annotations.

The @Controller annotation is used to define a controller and the @ResponseBody annotation is used to indicate that the return value of a method should be used as the response body of the request.

@RequestMapping("/api") declares that the url for all the apis in this controller will start with /api.

Let’s now look at the implementation of all the apis one by one.

1. Get All Notes (GET /api/notes)

// Get All Notes @GetMapping("/notes") public List<Note> getAllNotes() { return noteRepository.findAll(); } 

The above method is pretty straightforward. It calls JpaRepository’s findAll() method to retrieve all the notes from the database and returns the entire list.

Also, The @GetMapping("/notes") annotation is a short form of @RequestMapping(value="/notes", method=RequestMethod.GET).

2. Create a new Note (POST /api/notes)

// Create a new Note @PostMapping("/notes") public Note createNote(@Valid @RequestBody Note note) { return noteRepository.save(note); } 

The @RequestBody annotation is used to bind the request body with a method parameter.

The @Valid annotation makes sure that the request body is valid. Remember, we had marked Note’s title and content with @NotBlank annotation in the Note model?

If the request body doesn’t have a title or a content, then spring will return a 400 BadRequest error to the client.

3. Get a Single Note (Get /api/notes/{noteId})

// Get a Single Note @GetMapping("/notes/{id}") public Note getNoteById(@PathVariable(value = "id") Long noteId) { return noteRepository.findById(noteId) .orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId)); } 

The @PathVariable annotation, as the name suggests, is used to bind a path variable with a method parameter.

In the above method, we are throwing a ResourceNotFoundException whenever a Note with the given id is not found.

This will cause Spring Boot to return a 404 Not Found error to the client (Remember, we had added a @ResponseStatus(value = HttpStatus.NOT_FOUND) annotation to the ResourceNotFoundException class).

4. Update a Note (PUT /api/notes/{noteId})

// Update a Note @PutMapping("/notes/{id}") public Note updateNote(@PathVariable(value = "id") Long noteId, @Valid @RequestBody Note noteDetails) { Note note = noteRepository.findById(noteId) .orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId)); note.setTitle(noteDetails.getTitle()); note.setContent(noteDetails.getContent()); Note updatedNote = noteRepository.save(note); return updatedNote; } 

5. Delete a Note (DELETE /api/notes/{noteId})

// Delete a Note @DeleteMapping("/notes/{id}") public ResponseEntity<?> deleteNote(@PathVariable(value = "id") Long noteId) { Note note = noteRepository.findById(noteId) .orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId)); noteRepository.delete(note); return ResponseEntity.ok().build(); } 

Running the Application

We’ve successfully built all the apis for our application. Let’s now run the app and test the apis.

Just go to the root directory of the application and type the following command to run it -

$ mvn spring-boot:run

The application will start at Spring Boot’s default tomcat port 8080.

Great! Now, It’s time to test our apis using postman.

Testing the APIs

Creating a new Note using POST /api/notes API

Spring Boot JPA Hibernate Rest API - Create a Note

Retrieving all Notes using GET /api/notes API

Spring Boot JPA Hibernate Rest API - Retrieve All Notes

Retrieving a single Note using GET /api/notes/{noteId} API

Spring Boot JPA Hibernate Rest API - Get a Single Note

Updating a Note using PUT /api/notes/{noteId} API

Spring Boot JPA Hibernate Rest API - Update a Note

Deleting a Note using DELETE /api/notes/{noteId} API

Spring Boot JPA Hibernate Rest API - Delete a Note

More Resources

The application that we built in this article had only one domain model. If you want to learn how to build REST APIs in an application with more than one domain models exhibiting a one-to-many relationship between each other, then I highly recommend you to check out the following article -

Spring Boot, JPA, Hibernate One-To-Many mapping example

Also, Go through the following article to learn how to build a full stack application with authentication and authorization using Spring Boot, Spring Security and React -

Spring Boot + Spring Security + JWT + MySQL + React Full Stack Polling App - Part 1

Conclusion

Congratulations folks! We successfully built a Restful CRUD API using Spring Boot, Mysql, Jpa and Hibernate.

You can find the source code for this tutorial on my github repository. Feel free to clone the repository and build upon it.

Thank you for reading. Please ask any questions in the comment section below.

相关文章
相关标签/搜索