目录
0. 本文目的
产生背景:
本人要参加某个全国计算机比赛,要求在4小时内局域网环境内编写B/S程序。4个小时的比赛时间非常紧张,于是需要准备能快速并且最少配置的搭建开发框架。因为没有互联网,就无法百度,无法查阅资料,一旦配置出错,后果是灾难性的。赛前在各种网站上一直没有找到最简单的增删改查,有的都是浅尝辄止的Hello World。于是,赛后我觉得一定要自己做个最简洁最高效的CUAD。并且要把关键代码直接贴到博客里,而不仅仅是的放到压缩包里。
内容说明:
本文用最简的POM文件、最简的层次结构(Controller\Service\DAO\Model)、最简的DAO类、最简的实体类,采用Spring-DATA-JPA 、Thymeleaf技术来最终实现表的增删改查。若你发现你不会使用Spring Data JPA,不会使用Thymeleaf,不要紧很简单,看完案例,你会发现这些技术都很基础和简单。用上它们才是简洁制胜的法宝。
项目结构:
1. 软件及版本
IDE:IntelliJ IDEA 2017.3 专业版(收费版)。破解方法自寻。
Maven 3.6.1
2. 新建项目
此步非本文重点,不作截图,仅文字描述。我们这里使用从maven处新建项目,所以保证您和maven服务器之间有网络连接。无论您连接的是互联网maven官网,或是局域网内的maven服务器。请确保maven服务器上有本文所需要的全部 jar 包。
- File-New-Project-Maven-选中【Create From archetype】-找到【org.apache.maven-archetypes:maven-archetype-webapp】(注意别找错了,有类似的);
- 输入GroupId: com.zxb 和ArtifactId: testBoot,或自拟;
- 一直下一步,直至出现新的项目窗口。
- 把下文中的代码或直接粘贴,或新建文件后粘贴进去,再进行适应性修改。
3.最简POM
下面是最简的POM.xml。
<?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.zxb</groupId>
<artifactId>testBoot</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 指定Spring Boot的版本 2.0.4.RELEASE -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<dependencies>
<!-- 导入Spirng Boot web 所需的jar包 -->
<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>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
仅需配置 Spring boot、spring-boot-starter-web(可以连接为Spring Booter核心)、spring-boot-starter-data-jpa(ORM持久层接口)、spring-boot-starter-thymeleaf(Spring Boot推荐的替代JSP的模板引擎)、mysql-connector-jara(连接mysql)。
这五个jar包就足够搭建个完整的可以对MySQL进行增删改查,页面遍历复杂列表数据的网站系统了。
如果你觉得没学过 Thymeleaf模板引擎,可以先看下去,你会发现它其实很简单,和<c:if>之流类似。
POM.xml文件编写好后,然后在IDEA的maven窗口右键 Reimport 一下,IDEA会自动导入POM.xml中所需要的jar包。
4. Spring Boot启动类
MyMain.java
package com.zxb.controller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
/**
* @SpringBootApplication:标注一个主程序类,用来标明这是一个Spring Boot应用
*/
@EnableAutoConfiguration
@SpringBootApplication
@EnableJpaRepositories
public class MyMain {
// Spring应用启动起来
public static void main(String[] args) {
SpringApplication.run(MyMain.class,args);
}
}
启动类写完,就可 Run 一次了。可以在浏览器中打开 http://localhost:8080,可以看到有文字显示。
至此,Spring Boot版本的Hello World就完成了,可以说这些步骤毫无使用价值。下面看如何实现MySQL表的增删改查,包括model/Entity/POJO层、Service层、DAO层、Controller层设计。
5. 最简实体类
这个项目里,我们全使用注解,不需一个实体类映射文件,也不需要任何Mybatis的xml文件。简洁高效,这才是理想的开发。举个例子。
BookEntity.java
package com.zxb.controller.model;
import java.io.Serializable;
import javax.persistence.*;
@Entity
@Table(name="bookstore")
public class BookEntity implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
public String getStorename() {
return storename;
}
public void setStorename(String storename) {
this.storename = storename;
}
@Column(name = "storename")
protected String storename;
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
@Column(name = "location")
protected String location;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
6.最简DAO
看看这个DAO层(与数据库交互的层)有多简单,这全都是Spring Data JPA框架的功劳。
BookDAO.java
package com.zxb.controller.dao;
import com.zxb.controller.model.BookEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface BookDAO extends JpaRepository<BookEntity, Integer>{
public BookEntity findById(Long id);
public BookEntity save(BookEntity user);
@Query(value = "SELECT u FROM BookEntity u WHERE storename=:storename")
public BookEntity findName(@Param("storename") String storename);
@Query(value="select u FROM BookEntity u ")
public List<BookEntity> findAll();
}
当然,这里仅仅列举了最简单的几个函数,但从中可以窥见 Spring Data JPA技术为我们做了很多的工作。我们既然选用Spring Boot,却为何放这么好的工具不用呢,却手动去造轮子,重复编写 MyBaits 查询?
这里是使用了注解形式进行查询,注解中的查询语句既可以是Hibernate的HQL语句,也可以是 原声的 SQL语句。若想使用原生态的SQL语句,则可以使用这个注解:
@Query(value="select * from Book" ,native= true)
native=true的意思就是使用原生的SQL查询。
我们仔细看看这个写法。注意:这个 BookDAO 不是个类,而是个 Interface,继承了 JpaRepository 这个Spring Data JPA给我们设计好的数据库持久接口,我们可以打开这个 JpaRepository,看看它给我们已经做好了哪些工作。
package org.springframework.data.jpa.repository;
import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort var1);
List<T> findAllById(Iterable<ID> var1);
<S extends T> List<S> saveAll(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);
}
其实,这里面还有很多智能化的东西,我没有深入去研究。大家可以自己去搜索,Spring Data JPA的用法。下面我们看看这个DAO接口如何使用,我贴出Service层。
BookService.java
package com.zxb.controller.service;
import com.zxb.controller.dao.BookDAO;
import com.zxb.controller.model.BookEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookService {
@Autowired
private BookDAO bookDAO;
public void save(BookEntity bookEntity){
bookDAO.save(bookEntity);
}
public List list(){
return bookDAO.findAll();
}
}
如上图所示,我们的 BookDAO 接口是可以直接使用的,无需实现该接口的继承类。并且还可以直接使用 BookDAO 的父亲接口 ,也就是 Spring Data JPA给我们设计好的 JpaRepository 里面的通用方法,是不是超简单。
7. 普通的Controller
Controller层已经很简单,还是普通的用法。
TestContoller.java
package com.zxb.controller.control;
import com.zxb.controller.model.BookEntity;
import com.zxb.controller.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.awt.print.Book;
import java.util.List;
@Controller
@EnableAutoConfiguration
public class TestController {
@Autowired
private BookService bookService;
//系统首页
@RequestMapping("/")
@ResponseBody
public String index(){
return "Hello Spring Boot.访问http://localhost:8080/book/add进行新增测试;访问http://localhost:8080/book/list查看列表";
}
//跳转至录入页面
@RequestMapping("/book/add")
public ModelAndView add(){
System.out.print("add..............");
ModelAndView mv = new ModelAndView();
mv.setViewName("add");
return mv;
}
//跳转至列表页面
@RequestMapping("/book/list")
public ModelAndView list(){
List<BookEntity> bookList = bookService.list();
// for (int i = 0; i < bookList.size(); i++) {
// System.out.print("---------------------"+bookList.get(i).getId());
// }
ModelAndView mv = new ModelAndView();
mv.setViewName("list");
mv.addObject("bookList",bookList);
return mv;
}
//保存表单
@RequestMapping("/book/save")
public String save(@RequestParam(name="bookstore")
String storename, @RequestParam(name="location") String location){
BookEntity bookEntity = new BookEntity();
System.out.print(storename);
bookEntity.setStorename(storename);
bookEntity.setLocation(location);
bookService.save(bookEntity);
System.out.print(bookEntity.getStorename());
return "redirect:/book/list";
}
}
8.最简结构
我很反感这样的层次结构:
sevice/serviceImple、DAO/DAOImpl、BookEntity/BookEntity.hbm.xml/Mapper.xml。
我觉得软件应以生产效率为目标,而坚持非死板的教条主义。我也做过有些规模的软件项目,这样的结构并没有想象中的那样灵活,更多时候在出现需求变更时,我们直接全都是新建类、新建方法,全都是新建,并没有体现出接口和接口实现的作用。我不否认自己未严格按照最优设计去做。但还是要务实些,这些接口和接口实现不会在实践中出现。
我认为小项目结构更适合这个:
Contoller、单层Service、单层DAO、单层Model/Entity/POJO。
9.不要过度设计
一切为生产为实际服务。务实,一定要务实。项目大型程度决定设计复杂程度。效率第一。
10.最后
附上其它文件,使得你可以不用下载zip就能对整个结构有个清晰的了解:
最简配置文件 application.properties:
#使用Spring Boot默认的thymeleat模板引擎,可不用显示配置视图解析器,仅需将html等文件
#放在指定的默认目录: resources/template 下即可。切记resources 复数形式的s不可丢,否则报错。
#mysql
#注意设置MYSQL的sever/database/table必须全设置为utf-8
#注意设置IDEA的所有encoding编码均为utf-8
#注意本配置文件中的url必须加上?后的参数,否则会出现汉字乱码!
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driverclass=com.mysql.jdbc.Driver
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
这其中,连视图解析器都不用配置。只要你按Spring Boot的默认约定,把HTML都放在 resources/template 目录下。注意这个路径一个字母一个s都不能少。如果你写成 resource/template那就会报错,就需要在配置文件中配置视图解析器!
空无一物的 web.xml,这个仍是默认内容:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>
增加书籍页面 add.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
新增页面
<form action="/book/save" method="post">
书店名称:<input type="text" value="" name="bookstore"/>
书店位置:<input type="text" value="" name="location"/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
列表页面 list.html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns:th="http://www.thymeleaf.org">
<head >
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>列表页面
<p th:text="${name}"></p>
<ul th:each="item:${bookList}">
<li>id:<span th:text="${{item.id}}" /></li>
<li>书店:<span th:text="${item.storename}"</li>
<li>位置:<span th:text="${item.location}"</li>
</ul>
</body>
</html>
注意:
1. 列表页面,简单的使用了 thymeleaf 做遍历数据库。
2. 列表页面,使用 thymeleaf 需要在 <html> 根标签处加上 thymeleaf 的命名空间属性。
<html xmlns:th="http://www.thymeleaf.org">
不见此属性,thymeleaf 的标签是不会被识别和解析的。切记。