Spring Boot中集成SpringDataJPA和Free

2022-04-17  本文已影响0人  SpaceCat

本文中主要介绍一个集成了SpringDataJPA和FreeMarker的示例。

1、简单介绍

1.1 SpringDataJPA

1.1.1 jdbc介绍

一般来说,数据存储在关系型数据库,而java语言直接操作的是对象。Java访问数据库,一般来来说,可能需要写jdbc连接的代码。
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
我们安装好数据库之后,我们的应用程序也是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口实现,即对Connection等接口的实现类的jar文件。


image.png

从这个图中,可以看出jdbc也是一个非常优雅的设计实现,通过它,应用程序员无需关注不同数据库厂商的实现差异。

1.1.2 ORM介绍

JDBC连接相关的代码,对于应用程序原来说,还是比较繁琐的。这里出现了一列的ORM(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping,对象关系映射)框架:

1.1.3 JPA介绍

JPA(Java Persistence API)中文名 Java 持久层 API,是 JDK 5.0 注解或 XML 描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

Sun 引入新的 JPA ORM 规范出于两个原因:其一,简化现有 Java EE 和 Java SE 应用开发工作;其二,Sun 希望整合 ORM 技术,实现天下归一。

JPA 包括以下三方面的内容:

JPA 的宗旨是为 POJO 提供持久化标准规范,由此可见,经过这几年的实践探索,能够脱离容器独立运行,方便开发和测试的理念已经深入人心了。Hibernate 3.2+、TopLink 10.1.3 以及 OpenJPA、QueryDSL 都提供了 JPA 的实现,以及最后的 Spring 的整合 Spring Data JPA。目前互联网公司和传统公司大量使用了 JPA 的开发标准规范。


image.png

1.2 FreeMarker

FreeMarker是一款模板引擎:即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarker Template Language(FTL)。它是简单的,专用的语言, 不是像PHP那样成熟的编程语言。那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算,之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。


image.png

这种方式通常被称为MVC(模型视图控制器)模式,对于动态网页来说,是一种特别流行的模式。它帮助从开发人员(Java 程序员)中分离出网页设计师(HTML设计师)。设计师无需面对模板中的复杂逻辑,在没有程序员来修改或重新编译代码时,也可以修改页面的样式。
而FreeMarker最初的设计,是被用来在MVC模式的Web开发框架中生成HTML页面的,它没有被绑定到Servlet或HTML或任意Web相关的东西上。它也可以用于非Web应用环境中。
FreeMarker是免费的,基于Apache许可证2.0版本发布。

2、代码实现

2.1 创建基本的工程

pom.xml文件,中引入了jpa、freemarker和springboot等相关的依赖:

<?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>com.lfqy.springboot</groupId>
    <artifactId>FreemarkerTrial</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.2.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- springBoot JPA的起步依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
        <!-- MySQL连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>
    </dependencies>
</project>

配置文件中配置了freemarker模板信息、数据库连接信息等,application.properties

spring.datasource.username=root
spring.datasource.password=lfqylfqy
spring.datasource.url=jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#JPA Configuration:
spring.jpa.database=MySQL
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update

#设定ftl文件路径
spring.freemarker.template-loader-path=classpath:/templates

写一个springboot的启动类com/lfqy/freemarker/SpringBootTrialApplication.java

package com.lfqy.freemarker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootTrialApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootTrialApplication.class);
    }

}

写一个测试的Controller类com/lfqy/freemarker/controller/TrialController.java

package com.lfqy.freemarker.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TrialController {

    @RequestMapping("/hello")
    @ResponseBody
    public String hello(){
        return "springboot is ok!";
    }

}

实际上,到这里,工程就可以启动了。访问/hello返回springboot is ok!

2.2 数据库相关的内容

2.2.1 准备测试数据

mysql数据库中执行如下脚本,准备测试数据。创建一个user表,插入两条测试数据:

-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `password` varchar(50) DEFAULT NULL,
  `name` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'zhangsan', '123', '张三');
INSERT INTO `user` VALUES ('2', 'lisi', '123', '李四');
COMMIT;

2.2.2 pojo、DAO和service

写一个pojo类com/lfqy/freemarker/pojo/User.java

package com.lfqy.freemarker.pojo;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

/**
 * Created by chengxia on 2022/4/14.
 */

@Entity
public class User {
    // 主键
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // 用户名
    private String username;
    // 密码
    private String password;
    // 姓名
    private String name;

    //setter和getter方法

    public Long getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getName() {
        return name;
    }

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

DAO类直接继承的jpa的接口,com/lfqy/freemarker/dao/UserDao.java

package com.lfqy.freemarker.dao;

import com.lfqy.freemarker.pojo.User;

import org.springframework.data.jpa.repository.JpaRepository;

/**
 * Created by chengxia on 2022/4/14.
 */

public interface UserDao extends JpaRepository<User, Long> {
}

service的接口类,com/lfqy/freemarker/service/UserService.java

package com.lfqy.freemarker.service;

import com.lfqy.freemarker.pojo.User;

import java.util.Optional;

/**
 * Created by chengxia on 2022/4/14.
 */
public interface UserService {
    Optional<User> getUserById(long id);
}

service的接口实现类,com/lfqy/freemarker/service/impl/UserServiceImpl.java

package com.lfqy.freemarker.service.impl;

import com.lfqy.freemarker.dao.UserDao;
import com.lfqy.freemarker.pojo.User;
import com.lfqy.freemarker.service.UserService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Optional;

/**
 * Created by chengxia on 2022/4/14.
 */
@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserDao userDao;

    @Override
    public Optional<User> getUserById(long id) {
        return userDao.findById(id);
    }

}

在这个类中,直接调用了jpa的现成方法查询数据库。
JpaRepository查询方法解析流程说明:
Spring Data JPA框架在进行方法名解析时,会先把方法名多余的前缀截取掉
比如find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。这里的findById就是根据ID属性进行查找的意思。如下列出了一些例子:

Keyword Sample JPQL snippet
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age ⇐ ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull findByAgeIsNull … where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection age) … where x.age not in ?1
TRUE findByActiveTrue() … where x.active = true
FALSE findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

2.3 Controller和视图

写一个Controller,其中包含及直接将查询结果返回的方法,也包含一个将查询出的对象信息通过模板渲染成html例子的方法。
com/lfqy/freemarker/controller/UserController.java

package com.lfqy.freemarker.controller;

import com.lfqy.freemarker.pojo.User;
import com.lfqy.freemarker.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

/**
 * Created by chengxia on 2022/4/14.
 */

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/user/{id}")
    private User getUserById(@PathVariable long id) {

        User user = userService.getUserById(id).get();
        return  user;
    }

    @RequestMapping("/show/{idStr}")
    @ResponseBody
    private ModelAndView showUserById(@PathVariable String idStr) {


        long id = Long.parseLong(idStr);
        User user = userService.getUserById(id).get();

        ModelAndView mv = new ModelAndView();
        mv.addObject("user",user);
        mv.setViewName("userInfo");
        return  mv;
    }
}

这里的showUserById方法,返回的是一个名为userInfo的模板,这个模板是一个以userInfo命名的ftl文件。
templates/userInfo.ftl

<html>
<head>
    <title>User Info</title>
</head>
<body>
用户列表:<br>
<table border="1">
    <tr>
        <th>id</th>
        <th>username</th>
        <th>password</th>
        <th>name</th>
    </tr>
<#--<#list userList as user>-->
    <tr>
        <td>${user.id}</td>
        <td>${user.username}</td>
        <td>${user.password}</td>
        <td>${user.name}</td>
    </tr>
<#--</#list>-->
</table>

</body>
</html>

3、运行效果和提示

到这里,全部的编码就完成了。工程结构如下:

image.png
运行com.lfqy.freemarker.SpringBootTrialApplication,可以启动工程。
访问http://localhost:8080/hello
image.png

访问http://localhost:8080/user/1

image.png

访问http://localhost:8080/show/1

image.png

这里在之前调试的时候,访问http://localhost:8080/show/1,对应的Controller方法总会执行两遍,查了好久,发现是由于没有在pom文件中引入freemarker依赖,导致方法中返回的mv不会被认为是一个模板,而是会把模板名放到参数的位置,重新执行一般Controller方法。添加freemarker的pom依赖后,问题得以正常解决。

4、freemarker模板的嵌套示例

新增一个测试模板嵌套的Controller,com/lfqy/freemarker/controller/FtlIncludeController.java

package com.lfqy.freemarker.controller;

import com.lfqy.freemarker.pojo.User;
import com.lfqy.freemarker.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

/**
 * Created by chengxia on 2022/4/14.
 */

@RestController
public class FtlIncludeController {

    @RequestMapping("/index")
    private ModelAndView index() {
        ModelAndView mv = new ModelAndView();
        mv.setViewName("index");
        return mv;
    }

}

新增一个模板文件,其中嵌套了好多子模板。
templates/index.ftl

<html>
<head>
    <title>FreeMarker include demo page</title>
    <#include "pubjs.ftl">
    <script>
        function foo() {
            alert('This is alert of function foo!');
        }
        <#include "jsfunc.ftl">
        function jquerytest() {
            //alert($('#testp'));
            $('#testp').css("background","yellow");
        }
    </script>
</head>
<body>
<#include "top.ftl">
<br/>
<p id="testp">top middle line</p>
<br/>
<#include "top.ftl">
<br/>
<button onclick="foo()">ExecuteFoo</button>
<button onclick="bar()">ExecuteBar</button>
<button onclick="jquerytest()">jquerytest1</button>
<button onclick="$('#testp').css('background','blue');">jquerytest2</button>
<p>middle bottom line</p>
<br/>
<#include "bottom.ftl">
</body>
</html>

templates/top.ftl

<h2>This is top area.</h2>

templates/middle.ftl

<h2>This is middle area.</h2>

templates/bottomo.ftl

<h2>This is bottom area.</h2>

templates/jsfunc.ftl

function bar() {
alert('This is alert of function bar!');
}

templates/pubjs.ftl

<script src="jquery1.10.2.min.js"></script>

完成之后工程目录如下:


image.png

访问http://localhost:8080/index

image.png

点击页面按钮,运行都符合代码逻辑预期。

参考资料

上一篇下一篇

猜你喜欢

热点阅读