当前位置:   article > 正文

基于spring-boot&spring-data-jpa的web开发环境集成

spring boot spring data jpa web

新技术?

spring-boot并不是全新的技术栈,而是整合了spring的很多组件,并且以约定优先的原则进行组合。使用boot我们不需要对冗杂的配置文件进行管理,主需要用它的注解便可启用大部分web开发中所需要的功能。本篇就是基于boot来配置jpa和静态文件访问,进行web应用的开发。

模板or静态页面

最原始的jsp页面在springboot中已经不在默认支持,spring-boot默认使用thymeleaf最为模板。当然我们也可以使用freemark或者velocity等其他后端模板。但是按照前后端分离的良好设计,我们最好采用静态页面作为前端模板,这样前后端完全分离,把数据处理逻辑写在程序并提供接口供前端调用。这样的设计更加灵活和清晰。

项目搭建

我们将讨论项目的结构、application配置文件、静态页面处理、自定义filter,listener,servlet以及拦截器的使用。最后集中讨论jpa的配置和操作以及如何进行单元测试和打包部署。

项目结构

项目使用maven进行依赖管理和构建,整体结构如下图所示:

img

我们的HTML页面和资源文件都在resources/static下,打成jar包的时候static目录位于/BOOT-INF/classes/。

pom.xml

我们需要依赖下面这些包:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
  81. 81
  82. 82
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
  90. 90
  91. 91
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>gxf.dev</groupId>
  7. <artifactId>topology</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <packaging>jar</packaging>
  10. <parent>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter-parent</artifactId>
  13. <version>1.5.6.RELEASE</version>
  14. </parent>
  15. <dependencies>
  16. <dependency>
  17. <groupId>org.springframework.boot</groupId>
  18. <artifactId>spring-boot-starter-web</artifactId>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-data-jpa</artifactId>
  23. <exclusions>
  24. <exclusion>
  25. <groupId>org.apache.tomcat</groupId>
  26. <artifactId>tomcat-jdbc</artifactId>
  27. </exclusion>
  28. </exclusions>
  29. </dependency>
  30. <dependency>
  31. <groupId>mysql</groupId>
  32. <artifactId>mysql-connector-java</artifactId>
  33. </dependency>
  34. <dependency>
  35. <groupId>com.zaxxer</groupId>
  36. <artifactId>HikariCP</artifactId>
  37. </dependency>
  38. <!--test-->
  39. <dependency>
  40. <groupId>org.springframework.boot</groupId>
  41. <artifactId>spring-boot-test</artifactId>
  42. <scope>test</scope>
  43. </dependency>
  44. <dependency>
  45. <groupId>junit</groupId>
  46. <artifactId>junit</artifactId>
  47. <scope>test</scope>
  48. </dependency>
  49. <dependency>
  50. <groupId>org.springframework</groupId>
  51. <artifactId>spring-test</artifactId>
  52. <version>4.3.10.RELEASE</version>
  53. <scope>test</scope>
  54. </dependency>
  55. </dependencies>
  56. <repositories>
  57. <repository>
  58. <id>nexus-aliyun</id>
  59. <name>Nexus aliyun</name>
  60. <layout>default</layout>
  61. <url>http://maven.aliyun.com/nexus/content/groups/public</url>
  62. <snapshots>
  63. <enabled>false</enabled>
  64. </snapshots>
  65. <releases>
  66. <enabled>true</enabled>
  67. </releases>
  68. </repository>
  69. </repositories>
  70. <build>
  71. <plugins>
  72. <plugin>
  73. <groupId>org.springframework.boot</groupId>
  74. <artifactId>spring-boot-maven-plugin</artifactId>
  75. <configuration>
  76. <mainClass>gxf.dev.topology.Application</mainClass>
  77. </configuration>
  78. <executions>
  79. <execution>
  80. <goals>
  81. <goal>repackage</goal>
  82. </goals>
  83. </execution>
  84. </executions>
  85. </plugin>
  86. </plugins>
  87. </build>
  88. </project>

spring-boot-starter-parent使我们项目的父pom。
spring-boot-starter-web提供嵌入式tomcat容器,从而使项目可以通过打成jar包的方式直接运行。
spring-boot-starter-data-jpa引入了jpa的支持。
spring-boot-test和junit配合做单元测试。
mysql-connector-java和HikariCP做数据库的连接池的操作。
spring-boot-maven-plugin插件能把项目和依赖的jar包以及资源文件和页面一起打成一个可运行的jar(运行在内嵌的tomcat)

启动人口
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  1. package gxf.dev.topology;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.boot.web.servlet.ServletComponentScan;
  6. @SpringBootApplication
  7. @EnableAutoConfiguration
  8. @ServletComponentScan
  9. public class Application {
  10. public static void main(String[] args) {
  11. SpringApplication.run(Application.class);
  12. }
  13. }

这里ServletComponentScan注解是启用servlet3的servler和filter以及listener的支持,下面会提到该用法。要注意的是:不能引入@EnableWebMvc注解,否则需要重新配置视图和资源文件映射。这样就不符合我们的前后端分离的初衷了。

静态资源处理

spring-boot默认会去classpath下面的/static/,/public/ ,/resources/目录去读取静态资源。因此按照约定优先的原则,我们直接把我们应用的页面和资源文件直接放在/static下面,如下图所示:

img

这样我们访问系统主页就会自动加载index.html,而且它所引用的资源文件也会在static/下开始加载。

application.yml

我们在application配置文件中设置各种参数,它可以是传统的properties文件也可以使用yml来逐级配置。本文采用的第二种方式yml,如果不懂可以参考:https://baike.baidu.com/item/YAML/1067697?fr=aladdin。其内容如下:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  1. server:
  2. port: 8080
  3. context-path: /topology
  4. session:
  5. timeout: 30
  6. tomcat:
  7. uri-encoding: utf-8
  8. logging:
  9. level:
  10. root: info
  11. gxf.dev.topology: debug#当配置了loggin.path属性时,将在该路径下生成spring.log文件,即:此时使用默认的日志文件名spring.log
  12. #当配置了loggin.file属性时,将在指定路径下生成指定名称的日志文件。默认为项目相对路径,可以为logging.file指定绝对路径。
  13. #path: /home/gongxufan/logs
  14. file: topology.log
  15. spring:
  16. jpa:
  17. show-sql: true
  18. open-in-view: false
  19. hibernate:
  20. naming:
  21. #配置ddl建表字段和实体字段一致
  22. physical-strategy: gxf.dev.topology.config.RealNamingStrategyImpl
  23. ddl-auto: update
  24. properties:
  25. hibernate:
  26. format_sql: true
  27. show_sql: true
  28. dialect: org.hibernate.dialect.MySQL5Dialect
  29. datasource:
  30. url: jdbc:mysql://localhost:3306/topology
  31. driver-class-name: com.mysql.jdbc.Driver
  32. username: root
  33. password: qwe
  34. hikari:
  35. cachePrepStmts: true
  36. prepStmtCacheSize: 250
  37. prepStmtCacheSqlLimit: 2048
  38. useServerPrepStmts: true

使用idea开发工具在编辑器会有自动变量提示,这样非常方便进行参数的选择和查阅。

server

server节点可以配置容器的很多参数,比如:端口,访问路径、还有tomcat本身的一些参数。这里设置了session的超时以及编码等。

logging

日志级别可以定义到具体的哪个包路径,日志文件的配置要注意:path和file配置一个就行,file默认会在程序工作目录下生成,也可以置顶绝对路径进行指定。

datasource

这里使用号称性能最牛逼的连接池hikaricp,具体配置可以参阅其官网:http://brettwooldridge.github.io/HikariCP/

jpa

这里主要注意下strategy的配置,关系到自动建表时的字段命名规则。默认会生成带_划线分割entity的字段名(骆驼峰式)。

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  1. package gxf.dev.topology.config;
  2. /**
  3. * ddl-auto选项开启的时候生成表的字段命名策略,默认会按照骆驼峰式风格用_隔开每个单词
  4. * 这个类可以保证entity定义的字段名和数据表的字段一致
  5. * @auth gongxufan
  6. * @Date 2016/8/3
  7. **/
  8. import org.hibernate.boot.model.naming.Identifier;
  9. import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
  10. import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
  11. import java.io.Serializable;
  12. public class RealNamingStrategyImpl extends org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy implements Serializable {
  13. public static final PhysicalNamingStrategyStandardImpl INSTANCE = new PhysicalNamingStrategyStandardImpl();
  14. @Override
  15. public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {return new Identifier(name.getText(), name.isQuoted());
  16. }
  17. @Override
  18. public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
  19. return new Identifier(name.getText(), name.isQuoted());
  20. }
  21. }

注册web组件

1) 最新的spring-boot引入新的注解ServletComponentScan,使用它可以方便的配置Servlet3+的web组件。主要有下面这三个注解:

  1. 1
  2. 2
  3. 3
  1. @WebServlet
  2. @WebFilter
  3. @WebListener

只要把这些注解标记组件即可完成注册。

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  1. package gxf.dev.topology.filter;
  2. import org.springframework.core.annotation.Order;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. import javax.servlet.annotation.WebFilter;
  10. import java.io.IOException;
  11. /**
  12. * author:gongxufan
  13. * date:11/14/17
  14. **/
  15. @Order(1)
  16. @WebFilter(filterName ="loginFilter", urlPatterns = "/login")
  17. public class LoginFilter implements Filter {
  18. @Override
  19. public void init(FilterConfig filterConfig) throws ServletException {
  20. }
  21. @Override
  22. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  23. System.out.println("login rquest");
  24. chain.doFilter(request,response);
  25. }
  26. @Override
  27. public void destroy() {
  28. }
  29. }
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  1. package gxf.dev.topology.filter;
  2. import javax.servlet.annotation.WebListener;
  3. import javax.servlet.http.HttpSessionAttributeListener;
  4. import javax.servlet.http.HttpSessionBindingEvent;
  5. import javax.servlet.http.HttpSessionEvent;
  6. import javax.servlet.http.HttpSessionListener;
  7. /**
  8. * 自定义listener
  9. * Created by gongxufan on 2016/7/5.
  10. */
  11. @WebListener
  12. public class SessionListener implements HttpSessionListener,HttpSessionAttributeListener {
  13. @Override
  14. public void sessionCreated(HttpSessionEvent httpSessionEvent) {
  15. System.out.println("init");
  16. }
  17. @Override
  18. public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
  19. System.out.println("destroy");
  20. }
  21. @Override
  22. public void attributeAdded(HttpSessionBindingEvent se) {
  23. System.out.println(se.getName() + ":" + se.getValue());
  24. }
  25. @Override
  26. public void attributeRemoved(HttpSessionBindingEvent se) {
  27. }
  28. @Override
  29. public void attributeReplaced(HttpSessionBindingEvent se) {
  30. }
  31. }

2) 拦截器的使用
拦截器不是Servlet规范的标准组件,它跟上面的三个组件不在一个处理链上。拦截器是spring使用AOP实现的,对controller执行前后可以进行干预,直接结束请求处理。而且拦截器只能对流经dispatcherServlet处理的请求才生效,静态资源就不会被拦截。
下面顶一个拦截器:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  1. package gxf.dev.topology.filter;
  2. import org.springframework.web.servlet.HandlerInterceptor;
  3. import org.springframework.web.servlet.ModelAndView;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. /**
  7. * author:gongxufan
  8. * date:11/14/17
  9. **/
  10. public class LoginInterceptor implements HandlerInterceptor {
  11. @Override
  12. public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
  13. System.out.println("LoginInterceptor.preHandle()在请求处理之前进行调用(Controller方法调用之前)");
  14. // 只有返回true才会继续向下执行,返回false取消当前请求
  15. return true;
  16. }
  17. @Override
  18. public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
  19. System.out.println("LoginInterceptor.postHandle()请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)");
  20. }
  21. @Override
  22. public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
  23. System.out.println("LoginInterceptor.afterCompletion()在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
  24. }
  25. }

要想它生效则需要加入拦截器栈:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  1. package gxf.dev.topology.config;
  2. import gxf.dev.topology.filter.LoginInterceptor;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
  6. /**
  7. * author:gongxufan
  8. * date:11/14/17
  9. **/
  10. @Configuration
  11. public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
  12. @Override
  13. public void addInterceptors(InterceptorRegistry registry) {
  14. //在这可以配置controller的访问路径
  15. registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");
  16. super.addInterceptors(registry);
  17. }
  18. }

jpa操作

spring-boot已经集成了JPA的Repository封装,基于注解的事务处理等,我们只要按照常规的JPA使用方法即可。以Node表的操作为例:

  1. 定义entity
    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    8. 8
    9. 9
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    1. package gxf.dev.topology.entity;
    2. import com.fasterxml.jackson.annotation.JsonInclude;
    3. import javax.persistence.Entity;
    4. import javax.persistence.Id;
    5. import javax.persistence.Table;
    6. import java.io.Serializable;
    7. /**
    8. * Created by gongxufan on 2014/11/20.
    9. */
    10. @Entity
    11. @Table(name ="node")
    12. @JsonInclude(JsonInclude.Include.NON_NULL)
    13. public class Node implements Serializable {
    14. @Id
    15. private String id;
    16. private String elementType;
    17. private String x;
    18. private String y;
    19. private String width;
    20. private String height;
    21. private String alpha;
    22. private String rotate;
    23. private String scaleX;
    24. private String scaleY;
    25. private String strokeColor;
    26. private String fillColor;
    27. private String shadowColor;
    28. private String shadowOffsetX;
    29. private String shadowOffsetY;
    30. private String zIndex;
    31. private String text;
    32. private String font;
    33. private String fontColor;
    34. private String textPosition;
    35. private String textOffsetX;
    36. private String textOffsetY;
    37. private String borderRadius;
    38. private String deviceId;
    39. private String dataType;
    40. private String borderColor;
    41. private String offsetGap;
    42. private String childNodes;
    43. private String nodeImage;
    44. private String templateId;
    45. private String deviceA;
    46. private String deviceZ;
    47. private String lineType;
    48. private String direction;
    49. private String vmInstanceId;
    50. private String displayName;
    51. private String vmid;
    52. private String topoLevel;
    53. private String parentLevel;
    54. private Setring nextLevel;
    55. //getter&setter
    56. }

JsonInclude注解用于返回JOSN字符串是忽略为空的字段。

  1. 编写repository接口

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    1. package gxf.dev.topology.repository;
    2. import gxf.dev.topology.entity.Node;
    3. import org.springframework.data.repository.PagingAndSortingRepository;
    4. public interface NodeRepository extends PagingAndSortingRepository<Node, String> {
    5. }
  2. 编写Service

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    8. 8
    9. 9
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    1. package gxf.dev.topology.service;
    2. import gxf.dev.topology.entity.Node;
    3. import gxf.dev.topology.repository.NodeRepository;
    4. import gxf.dev.topology.repository.SceneRepository;
    5. import gxf.dev.topology.repository.StageRepository;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.stereotype.Component;
    8. import org.springframework.transaction.annotation.Transactional;
    9. /**
    10. * dao操作
    11. * author:gongxufan
    12. * date:11/13/17
    13. **/
    14. @Component
    15. public class TopologyService {
    16. @Autowired
    17. private NodeRepository nodeRepository;
    18. @Autowired
    19. private SceneRepository sceneRepository;
    20. @Autowired
    21. private StageRepository stageRepository;
    22. @Transactional
    23. public Node saveNode(Node node) {return nodeRepository.save(node);
    24. }
    25. public Iterable<Node> getAll() {
    26. return nodeRepository.findAll();
    27. }
    28. }

单元测试

单元测试使用spring-boot-test和junit进行,需要用到下面的几个注解:

  1. 1
  2. 2
  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest(classes = Application.class)

测试代码如下:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  1. import gxf.dev.topology.Application;
  2. import gxf.dev.topology.entity.Node;
  3. import gxf.dev.topology.repository.CustomSqlDao;
  4. import gxf.dev.topology.service.TopologyService;
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.boot.test.context.SpringBootTest;
  9. import org.springframework.test.context.junit4.SpringRunner;
  10. /**
  11. * author:gongxufan
  12. * date:11/13/17
  13. **/
  14. @RunWith(SpringRunner.class)
  15. @SpringBootTest(classes = Application.class)
  16. public class ServiceTest {
  17. @Autowired
  18. private TopologyService topologyService;
  19. @Autowired
  20. private CustomSqlDao customSqlDao;
  21. @Test
  22. public voidtestNode() {
  23. Node node = new Node();
  24. node.setId("node:2");
  25. node.setDisplayName("test1");
  26. topologyService.saveNode(node);
  27. }
  28. @Test
  29. public void testNative(){
  30. System.out.println(customSqlDao.querySqlObjects("select * from node"));
  31. System.out.println(customSqlDao.getMaxColumn("id","node"));
  32. }
  33. }

jpa补充

使用JPA进行单表操作确实很方便,但是对于多表连接的复杂查询可能不太方便。一般有两种方式弥补这个不足:

  1. 一个是在Query里标注为NativeQuery,直接使用原生SQL。不过这种方式在动态参数查询到额情况下很不方便,这时候我们需要按条件拼接SQL。
  2. 自定义DAO
    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    8. 8
    9. 9
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 63
    64. 64
    65. 65
    66. 66
    67. 67
    68. 68
    69. 69
    70. 70
    71. 71
    72. 72
    73. 73
    74. 74
    75. 75
    76. 76
    77. 77
    78. 78
    79. 79
    80. 80
    81. 81
    82. 82
    83. 83
    84. 84
    85. 85
    86. 86
    87. 87
    88. 88
    89. 89
    90. 90
    91. 91
    92. 92
    93. 93
    94. 94
    95. 95
    96. 96
    97. 97
    98. 98
    99. 99
    100. 100
    101. 101
    102. 102
    103. 103
    104. 104
    105. 105
    106. 106
    107. 107
    108. 108
    109. 109
    110. 110
    111. 111
    112. 112
    113. 113
    114. 114
    115. 115
    116. 116
    117. 117
    118. 118
    119. 119
    120. 120
    121. 121
    122. 122
    123. 123
    124. 124
    125. 125
    126. 126
    127. 127
    128. 128
    129. 129
    130. 130
    131. 131
    132. 132
    133. 133
    134. 134
    135. 135
    136. 136
    137. 137
    138. 138
    139. 139
    140. 140
    141. 141
    142. 142
    143. 143
    144. 144
    145. 145
    146. 146
    147. 147
    148. 148
    149. 149
    150. 150
    151. 151
    152. 152
    153. 153
    154. 154
    155. 155
    156. 156
    157. 157
    158. 158
    159. 159
    160. 160
    161. 161
    162. 162
    163. 163
    164. 164
    165. 165
    166. 166
    167. 167
    168. 168
    169. 169
    170. 170
    171. 171
    172. 172
    173. 173
    174. 174
    175. 175
    176. 176
    177. 177
    178. 178
    179. 179
    180. 180
    181. 181
    182. 182
    183. 183
    184. 184
    185. 185
    186. 186
    187. 187
    188. 188
    189. 189
    190. 190
    191. 191
    192. 192
    193. 193
    194. 194
    195. 195
    196. 196
    197. 197
    198. 198
    199. 199
    200. 200
    201. 201
    202. 202
    203. 203
    204. 204
    205. 205
    206. 206
    207. 207
    208. 208
    209. 209
    210. 210
    211. 211
    212. 212
    213. 213
    214. 214
    215. 215
    216. 216
    217. 217
    218. 218
    219. 219
    220. 220
    221. 221
    222. 222
    223. 223
    224. 224
    225. 225
    226. 226
    227. 227
    228. 228
    229. 229
    230. 230
    231. 231
    232. 232
    233. 233
    234. 234
    235. 235
    236. 236
    237. 237
    238. 238
    239. 239
    240. 240
    241. 241
    242. 242
    243. 243
    244. 244
    245. 245
    246. 246
    247. 247
    248. 248
    249. 249
    250. 250
    251. 251
    252. 252
    253. 253
    254. 254
    255. 255
    256. 256
    257. 257
    258. 258
    259. 259
    260. 260
    261. 261
    262. 262
    263. 263
    264. 264
    265. 265
    266. 266
    267. 267
    268. 268
    269. 269
    270. 270
    271. 271
    272. 272
    273. 273
    274. 274
    275. 275
    276. 276
    277. 277
    278. 278
    279. 279
    280. 280
    281. 281
    282. 282
    283. 283
    284. 284
    285. 285
    286. 286
    287. 287
    288. 288
    289. 289
    290. 290
    291. 291
    292. 292
    293. 293
    294. 294
    295. 295
    296. 296
    297. 297
    298. 298
    1. package gxf.dev.topology.repository;
    2. import com.mysql.jdbc.StringUtils;
    3. import org.hibernate.SQLQuery;
    4. import org.hibernate.criterion.CriteriaSpecification;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.stereotype.Component;
    7. import javax.persistence.EntityManager;
    8. import javax.persistence.EntityManagerFactory;
    9. import javax.persistence.Query;
    10. import java.math.BigDecimal;
    11. import java.util.ArrayList;
    12. import java.util.List;
    13. import java.util.Map;
    14. import java.util.regex.Matcher;
    15. import java.util.regex.Pattern;
    16. /**
    17. * 支持自定义SQL查询
    18. * Created by gongxufan on 2016/3/17.
    19. */
    20. @Component
    21. public class CustomSqlDao {
    22. @Autowired
    23. private EntityManagerFactory entityManagerFactory;
    24. public int getMaxColumn(final String filedName, final String tableName) {
    25. String sql ="select nvl(max(" + filedName + "), 0) as max_num from " + tableName;
    26. Map map = entityManagerFactory.getProperties();
    27. String dialect = (String) map.get("hibernate.dialect");
    28. //determine which database use
    29. if(!StringUtils.isNullOrEmpty(dialect)){
    30. if(dialect.contains("MySQL")){
    31. sql = "select ifnull(max(" + filedName + "), 0) as max_num from " + tableName;
    32. }
    33. if(dialect.contains("Oracle")){
    34. sql = "select nvl(max(" + filedName + "), 0) as max_num from " + tableName;
    35. }
    36. }
    37. int maxID = 0;
    38. List<Map<String, Object>> list = this.querySqlObjects(sql);
    39. if (list.size() > 0) {
    40. Object maxNum = list.get(0).get("max_num");
    41. if(maxNum instanceof Number)
    42. maxID = ((Number)maxNum).intValue();
    43. if(maxNum instanceof String)
    44. maxID = Integer.valueOf((String)maxNum);
    45. }
    46. return maxID + 1;
    47. }
    48. public List<Map<String, Object>> querySqlObjects(String sql, Integer currentPage, Integer rowsInPage) {
    49. return this.querySqlObjects(sql, null, currentPage, rowsInPage);
    50. }
    51. public List<Map<String, Object>> querySqlObjects(String sql) {
    52. return this.querySqlObjects(sql, null, null, null);
    53. }
    54. public List<Map<String, Object>> querySqlObjects(String sql, Map params) {
    55. return this.querySqlObjects(sql, params, null, null);
    56. }
    57. @SuppressWarnings("unchecked")
    58. public List<Map<String, Object>> querySqlObjects(String sql, Object params, Integer currentPage, Integer rowsInPage) {
    59. EntityManager entityManager = entityManagerFactory.createEntityManager();
    60. Query qry = entityManager.createNativeQuery(sql);
    61. SQLQuery s = qry.unwrap(SQLQuery.class);
    62. //设置参数
    63. if (params != null) {
    64. if (params instanceof List) {
    65. List<Object> paramList = (List<Object>) params;
    66. for (int i = 0, size = paramList.size(); i < size; i++) {
    67. qry.setParameter(i + 1, paramList.get(i));
    68. }
    69. } else if (params instanceof Map) {
    70. Map<String, Object> paramMap = (Map<String, Object>) params;
    71. Object o = null;
    72. for (String key : paramMap.keySet()) {
    73. o = paramMap.get(key);
    74. if (o != null)
    75. qry.setParameter(key, o);
    76. }
    77. }
    78. }
    79. if (currentPage != null && rowsInPage != null) {//判断是否有分页
    80. // 起始对象位置
    81. qry.setFirstResult(rowsInPage * (currentPage - 1));
    82. // 查询对象个数
    83. qry.setMaxResults(rowsInPage);
    84. }
    85. s.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);
    86. List<Map<String, Object>> resultList = new ArrayList<Map<String, Object>>();
    87. try {
    88. List list = qry.getResultList();
    89. resultList = s.list();
    90. } catch (Exception e) {
    91. e.printStackTrace();
    92. } finally {
    93. entityManager.close();
    94. }
    95. return resultList;
    96. }
    97. public int getCount(String sql) {
    98. String sqlCount = "select count(0) as count_num from " + sql;
    99. List<Map<String, Object>> list = this.querySqlObjects(sqlCount);
    100. if (list.size() > 0) {
    101. int countNum = ((BigDecimal) list.get(0).get("COUNT_NUM")).intValue();
    102. return countNum;
    103. } else {
    104. return 0;
    105. }
    106. }
    107. /**
    108. * 处理sql语句
    109. *
    110. * @param _strSql
    111. * @return
    112. */
    113. public String toSql(String _strSql) {
    114. String strNewSql = _strSql;
    115. if (strNewSql != null) {
    116. strNewSql = regReplace("'", "''", strNewSql);
    117. } else {
    118. strNewSql = "";
    119. }
    120. return strNewSql;
    121. }
    122. private String regReplace(String strFind, String strReplacement, String strOld) {
    123. String strNew = strOld;
    124. Pattern p = null;
    125. Matcher m = null;
    126. try {
    127. p = Pattern.compile(strFind);
    128. m = p.matcher(strOld);
    129. strNew = m.replaceAll(strReplacement);
    130. } catch (Exception e) {
    131. }
    132. return strNew;
    133. }
    134. /**
    135. * 根据hql语句查询数据
    136. *
    137. * @param hql
    138. * @return
    139. */
    140. @SuppressWarnings("rawtypes")
    141. public List queryForList(String hql, List<Object> params) {
    142. EntityManager entityManager = entityManagerFactory.createEntityManager();
    143. Query query = entityManager.createQuery(hql);
    144. List list = null;
    145. try {
    146. if (params != null && !params.isEmpty()) {
    147. for (int i = 0, size = params.size(); i < size; i++) {
    148. query.setParameter(i + 1, params.get(i));
    149. }
    150. }
    151. list = query.getResultList();
    152. } catch (Exception e) {
    153. e.printStackTrace();
    154. } finally {
    155. entityManager.close();
    156. }
    157. return list;
    158. }
    159. @SuppressWarnings("rawtypes")
    160. public List queryByMapParams(String hql, Map<String, Object> params, Integer currentPage, Integer pageSize) {
    161. EntityManager entityManager = entityManagerFactory.createEntityManager();
    162. Query query = entityManager.createQuery(hql);
    163. List list = null;
    164. try {
    165. if (params != null && !params.isEmpty()) {
    166. for (Map.Entry<String, Object> entry : params.entrySet()) {
    167. query.setParameter(entry.getKey(), entry.getValue());
    168. }
    169. }
    170. if (currentPage != null && pageSize != null) {
    171. query.setFirstResult((currentPage - 1) * pageSize);
    172. query.setMaxResults(pageSize);
    173. }
    174. list = query.getResultList();
    175. } catch (Exception e) {
    176. e.printStackTrace();
    177. } finally {
    178. entityManager.close();
    179. }
    180. return list;
    181. }
    182. @SuppressWarnings("rawtypes")
    183. public List queryByMapParams(String hql, Map<String, Object> params) {
    184. return queryByMapParams(hql, params, null, null);
    185. }
    186. @SuppressWarnings("rawtypes")
    187. public List queryForList(String hql) {
    188. return queryForList(hql, null);
    189. }
    190. /**
    191. * 查询总数
    192. *
    193. * @param hql
    194. * @param params
    195. * @return
    196. */
    197. public Long queryCount(String hql, Map<String, Object> params) {
    198. EntityManager entityManager = entityManagerFactory.createEntityManager();
    199. Query query = entityManager.createQuery(hql);
    200. Long count = null;
    201. try {
    202. if (params != null && !params.isEmpty()) {
    203. for (Map.Entry<String, Object> entry : params.entrySet()) {
    204. query.setParameter(entry.getKey(), entry.getValue());
    205. }
    206. }
    207. count = (Long) query.getSingleResult();
    208. } catch (Exception e) {
    209. e.printStackTrace();
    210. } finally {
    211. entityManager.close();
    212. }
    213. return count;
    214. }
    215. /**
    216. * 查询总数
    217. *
    218. * @param sql
    219. * @param params
    220. * @return
    221. */
    222. public Integer queryCountBySql(String sql, Map<String, Object> params) {
    223. EntityManager entityManager = entityManagerFactory.createEntityManager();
    224. Integer count = null;
    225. try {
    226. Query query = entityManager.createNativeQuery(sql);
    227. if (params != null && !params.isEmpty()) {
    228. for (Map.Entry<String, Object> entry : params.entrySet()) {
    229. query.setParameter(entry.getKey(), entry.getValue());
    230. }
    231. }
    232. Object obj = query.getSingleResult();
    233. if (obj instanceof BigDecimal) {
    234. count = ((BigDecimal) obj).intValue();
    235. } else {
    236. count = (Integer) obj;
    237. }
    238. } finally {
    239. if (entityManager != null) {
    240. entityManager.close();
    241. }
    242. }
    243. return count;
    244. }
    245. /**
    246. * select count(*) from table
    247. *
    248. * @param sql
    249. * @param params
    250. * @return
    251. */
    252. public int executeSql(String sql, List<Object> params) {
    253. EntityManager entityManager = entityManagerFactory.createEntityManager();
    254. try {
    255. Query query = entityManager.createNativeQuery(sql);
    256. if (params != null && !params.isEmpty()) {
    257. for (int i = 0, size = params.size(); i < size; i++) {
    258. query.setParameter(i + 1, params.get(i));
    259. }
    260. }
    261. return query.executeUpdate();
    262. } finally {
    263. if (entityManager != null) {
    264. entityManager.close();
    265. }
    266. }
    267. }
    268. }

我们在service层注入,然后就可以根据输入条件拼接好sql或者hql来进行各种操作。这种方式灵活而且也不需要手动写分页代码,使用hibernate封装好的机制即可。

总结

使用boot可以快速搭建一个前后端开发的骨架,里面有很多自动的配置和约定。虽然boot不是新的一个技术栈,但是它要求我们对各个组件都要比较熟悉,不然对它的运行机制和约定配置会感到很困惑。而使用JPA进行数据库操作也是利弊参半,需要自己权衡。

项目代码:https://github.com/gongxufan/topology

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/784152
推荐阅读
相关标签
  

闽ICP备14008679号