基於spring-boot&spring-data-jpa的web開發環境集成

新技術?

spring-boot並非全新的技術棧,而是整合了spring的不少組件,而且以約定優先的原則進行組合。使用boot咱們不須要對冗雜的配置文件進行管理,主須要用它的註解即可啓用大部分web開發中所須要的功能。本篇就是基於boot來配置jpa和靜態文件訪問,進行web應用的開發。html

模板or靜態頁面

最原始的jsp頁面在springboot中已經不在默認支持,spring-boot默認使用thymeleaf最爲模板。固然咱們也可使用freemark或者velocity等其餘後端模板。可是按照先後端分離的良好設計,咱們最好採用靜態頁面做爲前端模板,這樣先後端徹底分離,把數據處理邏輯寫在程序並提供接口供前端調用。這樣的設計更加靈活和清晰。前端

項目搭建

咱們將討論項目的結構、application配置文件、靜態頁面處理、自定義filter,listener,servlet以及攔截器的使用。最後集中討論jpa的配置和操做以及如何進行單元測試和打包部署。java

項目結構

項目使用maven進行依賴管理和構建,總體結構以下圖所示:
img
咱們的HTML頁面和資源文件都在resources/static下,打成jar包的時候static目錄位於/BOOT-INF/classes/。node

pom.xml

咱們須要依賴下面這些包:
mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
複製代碼
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>gxf.dev</groupId>
    <artifactId>topology</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.6.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.tomcat</groupId>
                    <artifactId>tomcat-jdbc</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

      <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
        </dependency>

        <!--test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.3.10.RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>nexus-aliyun</id>
            <name>Nexus aliyun</name>
            <layout>default</layout>
            <url>http://maven.aliyun.com/nexus/content/groups/public</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
    </repositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>gxf.dev.topology.Application</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</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)git

啓動人口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
複製代碼
package gxf.dev.topology;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@EnableAutoConfiguration
@ServletComponentScan
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}
複製代碼

這裏ServletComponentScan註解是啓用servlet3的servler和filter以及listener的支持,下面會提到該用法。要注意的是:不能引入@EnableWebMvc註解,不然須要從新配置視圖和資源文件映射。這樣就不符合咱們的先後端分離的初衷了。github

靜態資源處理

spring-boot默認會去classpath下面的/static/,/public/ ,/resources/目錄去讀取靜態資源。所以按照約定優先的原則,咱們直接把咱們應用的頁面和資源文件直接放在/static下面,以下圖所示:
img
這樣咱們訪問系統主頁就會自動加載index.html,並且它所引用的資源文件也會在static/下開始加載。web

application.yml

咱們在application配置文件中設置各類參數,它能夠是傳統的properties文件也可使用yml來逐級配置。本文采用的第二種方式yml,若是不懂能夠參考:baike.baidu.com/item/YAML/1…。其內容以下:
spring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
複製代碼
server:
    port: 8080
    context-path: /topology
    session:
      timeout: 30
    tomcat:
      uri-encoding: utf-8

logging:
    level:
        root: info
        gxf.dev.topology: debug
        #當配置了loggin.path屬性時,將在該路徑下生成spring.log文件,即:此時使用默認的日誌文件名spring.log
        #當配置了loggin.file屬性時,將在指定路徑下生成指定名稱的日誌文件。默認爲項目相對路徑,能夠爲logging.file指定絕對路徑。
        #path: /home/gongxufan/logs
    file: topology.log

spring:
    jpa:
      show-sql: true
      open-in-view: false
      hibernate:
        naming:
          #配置ddl建表字段和實體字段一致
          physical-strategy: gxf.dev.topology.config.RealNamingStrategyImpl
          ddl-auto: update
      properties:
        hibernate:
          format_sql: true
          show_sql: true
          dialect: org.hibernate.dialect.MySQL5Dialect
    datasource:
        url: jdbc:mysql://localhost:3306/topology
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: qwe
        hikari:
              cachePrepStmts: true
              prepStmtCacheSize: 250
              prepStmtCacheSqlLimit: 2048
              useServerPrepStmts: true
複製代碼

使用idea開發工具在編輯器會有自動變量提示,這樣很是方便進行參數的選擇和查閱。sql

server

server節點能夠配置容器的不少參數,好比:端口,訪問路徑、還有tomcat自己的一些參數。這裏設置了session的超時以及編碼等。

logging

日誌級別能夠定義到具體的哪一個包路徑,日誌文件的配置要注意:path和file配置一個就行,file默認會在程序工做目錄下生成,也能夠置頂絕對路徑進行指定。

datasource

這裏使用號稱性能最牛逼的鏈接池hikaricp,具體配置能夠參閱其官網:brettwooldridge.github.io/HikariCP/

jpa

這裏主要注意下strategy的配置,關係到自動建表時的字段命名規則。默認會生成帶_劃線分割entity的字段名(駱駝峯式)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
複製代碼
package gxf.dev.topology.config;

/**
 * ddl-auto選項開啓的時候生成表的字段命名策略,默認會按照駱駝峯式風格用_隔開每一個單詞
 * 這個類能夠保證entity定義的字段名和數據表的字段一致
 * @auth gongxufan
 * @Date 2016/8/3
 **/

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

import java.io.Serializable;


public class RealNamingStrategyImpl extends org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy implements Serializable {

    public static final PhysicalNamingStrategyStandardImpl INSTANCE = new PhysicalNamingStrategyStandardImpl();

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        return new Identifier(name.getText(), name.isQuoted());
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        return new Identifier(name.getText(), name.isQuoted());
    }

}
複製代碼

註冊web組件

1) 最新的spring-boot引入新的註解ServletComponentScan,使用它能夠方便的配置Servlet3+的web組件。主要有下面這三個註解:

1
2
3
複製代碼
@WebServlet
@WebFilter
@WebListener
複製代碼

只要把這些註解標記組件便可完成註冊。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
複製代碼
package gxf.dev.topology.filter;

import org.springframework.core.annotation.Order;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * author:gongxufan
 * date:11/14/17
 **/
@Order(1)
@WebFilter(filterName = "loginFilter", urlPatterns = "/login")
public class LoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("login rquest");
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {

    }
}
複製代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
複製代碼
package gxf.dev.topology.filter;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * 自定義listener
 * Created by gongxufan on 2016/7/5.
 */
@WebListener
public class SessionListener implements HttpSessionListener,HttpSessionAttributeListener {

    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        System.out.println("init");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        System.out.println("destroy");
    }

    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        System.out.println(se.getName() + ":" + se.getValue());
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {

    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {

    }
}
複製代碼

2) 攔截器的使用
攔截器不是Servlet規範的標準組件,它跟上面的三個組件不在一個處理鏈上。攔截器是spring使用AOP實現的,對controller執行先後能夠進行干預,直接結束請求處理。並且攔截器只能對流經dispatcherServlet處理的請求才生效,靜態資源就不會被攔截。
下面頂一個攔截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
複製代碼
package gxf.dev.topology.filter;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * author:gongxufan
 * date:11/14/17
 **/
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("LoginInterceptor.preHandle()在請求處理以前進行調用(Controller方法調用以前)");
        // 只有返回true纔會繼續向下執行,返回false取消當前請求
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("LoginInterceptor.postHandle()請求處理以後進行調用,可是在視圖被渲染以前(Controller方法調用以後)");
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("LoginInterceptor.afterCompletion()在整個請求結束以後被調用,也就是在DispatcherServlet 渲染了對應的視圖以後執行(主要是用於進行資源清理工做)");
    }
}
複製代碼

要想它生效則須要加入攔截器棧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
複製代碼
package gxf.dev.topology.config;

import gxf.dev.topology.filter.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * author:gongxufan
 * date:11/14/17
 **/
@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	//在這能夠配置controller的訪問路徑
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");
        super.addInterceptors(registry);
    }
}
複製代碼

jpa操做

spring-boot已經集成了JPA的Repository封裝,基於註解的事務處理等,咱們只要按照常規的JPA使用方法便可。以Node表的操做爲例:

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

JsonInclude註解用於返回JOSN字符串是忽略爲空的字段。

  1. 編寫repository接口

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

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

單元測試

單元測試使用spring-boot-test和junit進行,須要用到下面的幾個註解:

1
2
複製代碼
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
複製代碼

測試代碼以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
複製代碼
import gxf.dev.topology.Application;
import gxf.dev.topology.entity.Node;
import gxf.dev.topology.repository.CustomSqlDao;
import gxf.dev.topology.service.TopologyService;
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.test.context.junit4.SpringRunner;

/**
 * author:gongxufan
 * date:11/13/17
 **/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class ServiceTest {
    @Autowired
    private TopologyService topologyService;

    @Autowired
    private CustomSqlDao customSqlDao;
    @Test
    public void testNode() {
        Node node = new Node();
        node.setId("node:2");
        node.setDisplayName("test1");
        topologyService.saveNode(node);
    }

    @Test
    public void testNative(){
        System.out.println(customSqlDao.querySqlObjects("select * from node"));
        System.out.println(customSqlDao.getMaxColumn("id","node"));
    }
}
複製代碼

jpa補充

使用JPA進行單表操做確實很方便,可是對於多表鏈接的複雜查詢可能不太方便。通常有兩種方式彌補這個不足:

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

咱們在service層注入,而後就能夠根據輸入條件拼接好sql或者hql來進行各類操做。這種方式靈活並且也不須要手動寫分頁代碼,使用hibernate封裝好的機制便可。

總結

使用boot能夠快速搭建一個先後端開發的骨架,裏面有不少自動的配置和約定。雖然boot不是新的一個技術棧,可是它要求咱們對各個組件都要比較熟悉,否則對它的運行機制和約定配置會感到很困惑。而使用JPA進行數據庫操做也是利弊參半,須要本身權衡。

項目代碼:github.com/gongxufan/t…

相關文章
相關標籤/搜索