3.Springboot界面设计
用SpringBoot框架设计Web显示界面,我们还是使用MVC(Model View Controller)的概念。将数据管理,事件控制和界面显示进行分层处理,实现多层结构设计。
-
视图设计
主要是组织和处理显示的内容
-
控制器
界面上的事件响应最终交给了控制器进行处理,由控制器决定是否调用模型进行数据的存取操作,然后再将结果返回给合适的视图显示。
3.1 模型设计
数据管理模块实现了MVC中模型的设计,主要负责实体建模和数据库持久化等方面的功能。
3.1.1 节点实体建模
Movie Node Entity
@JsonIdentityInfo(generator=JSOGGenerator.class) // 防止递归依赖
@NodeEntity
public class Movie {
@GraphId
Long id; // 表明唯一性
private String name;
private String photo;
//因为Neo4j还没有日期格式的数据类型,所以在读取日期类型的数据时,使用注解@DateTimeFormat进行格式转换,而在保存时,使用注解@DateLong将它转换成Long类型的数据进行存储。
@DateLong
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createDate;
@Relationship(type="扮演", direction = Relationship.INCOMING)
List<Role> roles = new ArrayList<>();
public Role addRole(Actor actor, String name){
Role role = new Role(actor,this,name);
this.roles.add(role);
return role;
}
public Movie() { }
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhoto() {
return photo;
}
public void setPhoto(String photo) {
this.photo = photo;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
}
Actor
@JsonIdentityInfo(generator=JSOGGenerator.class)
@NodeEntity
public class Actor {
@GraphId
Long id;
private String name;
private int sex;
@DateLong
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date born;
public Actor() { }
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public Date getBorn() {
return born;
}
public void setBorn(Date born) {
this.born = born;
}
}
3.1.2 关系实体建模
Role
@JsonIdentityInfo(generator=JSOGGenerator.class)
@RelationshipEntity(type = "扮演")
public class Role {
@GraphId
Long id;
String name;
@StartNode
Actor actor;
@EndNode
Movie movie;
public Role() {
}
public Role(Actor actor, Movie movie, String name) {
this.actor = actor;
this.movie = movie;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Actor getActor() {
return actor;
}
public Movie getMovie() {
return movie;
}
}
3.1.3 分页查询设计
对于新型的Neo4j数据库来说,由于它的资源库遵循了JPA的规范标准阿来设计,在分页查询方面有的地方还不是很完善。所以在分页查询中,设计了一个服务类来处理。
PagesService.java
@Service
public class PagesService<T> {
@Autowired
private Session session;
/*
*使用Class<T>传入调用的实体对象,使用Pageable传入页数设定和排序字段设定的参数。使用Filters传入查询的一些节点属性设定的参数
**/
public Page<T> findAll(Class<T> clazz, Pageable pageable, Filters filters){
Collection data = this.session.loadAll(clazz, filters, convert(pageable.getSort()), new Pagination(pageable.getPageNumber(), pageable.getPageSize()), 1);
return updatePage(pageable, new ArrayList(data));
}
private Page<T> updatePage(Pageable pageable, List<T> results) {
int pageSize = pageable.getPageSize();
int pageOffset = pageable.getOffset();
int total = pageOffset + results.size() + (results.size() == pageSize?pageSize:0);
return new PageImpl(results, pageable, (long)total);
}
private SortOrder convert(Sort sort) {
SortOrder sortOrder = new SortOrder();
if(sort != null) {
Iterator var3 = sort.iterator();
while(var3.hasNext()) {
Sort.Order order = (Sort.Order)var3.next();
if(order.isAscending()) {
sortOrder.add(new String[]{order.getProperty()});
} else {
sortOrder.add(SortOrder.Direction.DESC, new String[]{order.getProperty()});
}
}
}
return sortOrder;
}
}
至于,该代码中其他的东西,暂未研究。
3.2 控制器设计
怎样将视图上的操作与模型— (数据管理模块)联系起来,这中间始终是控制器起着通信桥梁的作用,它响应视图上的操作事件。然后根据需要决定是否访问数据管理模块,最后再将结果返回给合适的视图,由视图处理显示。
下面按照Movie控制器的设计来说明控制器中增删查改的实现方法,演员控制器的设计与此类似。
MovieController.java
@RestController
@RequestMapping("/movie")
public class MovieController {
private static Logger logger = LoggerFactory.getLogger(MovieController.class);
@Autowired
private MovieRepository movieRepository;
@Autowired
private ActorRepository actorRepository;
@Autowired
private PagesService<Movie> pagesService;
@RequestMapping("/index")
public ModelAndView index(){
return new ModelAndView("movie/index");
}
/*
查看一个电影的详细信息时,控制器首先使用请求的电影ID向数据管理模块请求数据,然后将取得的数据输出到一个显示视图上。
*/
@RequestMapping(value="/{id}")
public ModelAndView show(ModelMap model, @PathVariable Long id) {
Movie movie = movieRepository.findOne(id);
model.addAttribute("movie",movie);
return new ModelAndView("movie/show");
}
/*
*接受新建电影的请求,以及输入一部电影的数据后的最后提交,由新建控制器进行处理。
在控制器上将执行两个操作,第一个操作将返回一个新建电影的视图,第二个操作接收界面中的输入数据,并调用数据管理模块进行保存。
create函数将返回一个新建电影的视图,它不调用数据管理模块
*/
@RequestMapping("/new")
public ModelAndView create(ModelMap model){
String[] files = {"/images/movie/西游记.jpg","/images/movie/西游记续集.jpg"};
model.addAttribute("files",files);
return new ModelAndView("movie/new");
}
/*
*save函数将需要保存的数据通过调用数据管理模块存储至数据库中,并返回一个成功标志。
*/
@RequestMapping(value="/save", method = RequestMethod.POST)
public String save(Movie movie) throws Exception{
movieRepository.save(movie);
logger.info("新增->ID={}", movie.getId());
return "1";
}
/*
若要实现对电影的修改及保存操作,需要先将电影的数据展示在视图界面上,然后接收界面的操作,调用数据管理模块将更改的数据保存至数据库中。
*/
@RequestMapping(value="/edit/{id}")
public ModelAndView update(ModelMap model, @PathVariable Long id){
Movie movie = movieRepository.findOne(id);
String[] files = {"/images/movie/西游记.jpg","/images/movie/西游记续集.jpg"};
String[] rolelist = new String[]{"唐僧","孙悟空","猪八戒","沙僧"};
Iterable<Actor> actors = actorRepository.findAll();
model.addAttribute("files", files);
model.addAttribute("rolelist",rolelist);
model.addAttribute("movie",movie);
model.addAttribute("actors",actors);
return new ModelAndView("movie/edit");
}
@RequestMapping(method = RequestMethod.POST, value="/update")
public String update(Movie movie, HttpServletRequest request) throws Exception{
String rolename = request.getParameter("rolename");
String actorid = request.getParameter("actorid");
Movie old = movieRepository.findOne(movie.getId());
old.setName(movie.getName());
old.setPhoto(movie.getPhoto());
old.setCreateDate(movie.getCreateDate());
if(!StringUtils.isEmpty(rolename) && !StringUtils.isEmpty(actorid)) {
Actor actor = actorRepository.findOne(new Long(actorid));
old.addRole(actor, rolename);
}
movieRepository.save(old);
logger.info("修改->ID="+old.getId());
return "1";
}
/*删除电影时,从界面上接收电影的ID参数,然后调用数据管理模块将电影删除*/
@RequestMapping(value="/delete/{id}",method = RequestMethod.GET)
public String delete(@PathVariable Long id) throws Exception{
Movie movie = movieRepository.findOne(id);
movieRepository.delete(movie);
logger.info("删除->ID="+id);
return "1";
}
/*
分页查询控制器,列表数据的查询使用分页的方法,按照提供的查询字段参数、页码、页大小及其排序子弹等参数,通过调用数据管理模块进行查询,然后返回一个分页对象Page
*/
@RequestMapping(value="/list")
public Page<Movie> list(HttpServletRequest request) throws Exception{
String name = request.getParameter("name");
String page = request.getParameter("page");
String size = request.getParameter("size");
Pageable pageable = new PageRequest(page==null? 0: Integer.parseInt(page), size==null? 10:Integer.parseInt(size),
new Sort(Sort.Direction.DESC, "id"));
Filters filters = new Filters();
if (!StringUtils.isEmpty(name)) {
Filter filter = new Filter("name", name);
filters.add(filter);
}
return pagesService.findAll(Movie.class, pageable, filters);
}
}
上面的代码中有很多搞不懂,暂未研究
3.3 使用Thymeleaf模板
使用SpringBoot框架进行界面设计,一般都会选择Thymeleaf模板
3.3.1 Thymeleat配置
要使用Thymeleaf模板,首先,必须在工程的Maven管理中引入它的依赖:
spring-boot-starter-thymeleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
其次,必须配置使用Thymeleaft模板的一些参数。在一般的Web项目中都会使用如下的配置
spring:
thymeleaf:
prefix:/WEB-INF/views/ #prefix指定了HTML文件存放在webapp的/WEB-INF/views/目录下面
suffix:.html
mode:HTML5
encoding:UTF-8
content-type:text/html
cache:false
为了更方便地将项目发不成jar文件,我们将使用Thymelea自动配置中的默认选项。即
我们只需要在resources文件夹中增加一个templates目录即可。这个目录用来存放HTML文件。
3.3.2 Thymeleaf功能简介
在HTML页面上使用Thymeleaf标签语言,用一个简单的关键字th来标注。
例如:
<h3 th:text:"${actor.name}"></h3> <!--指定了标签h3中显示的文本,它的值来自于关键字$所引用的内存变量-->
<img th:src = "@{/images/logo.png}"/><!--设定了标签<img>的图片文件的链接地址-->
Thymeleaf的一些主要标签和函数:
th:text 显示文本
th:utext:和th:text的区别是针对"unescaped text".
th:attr:设置标签属性
th:if or th:unless:条件判断语句
th:switch, th:case:选择语句
th:each:循环语句
#dates:日期函数
#calendars:日历函数
#numbers:数字函数
#strings:字符串函数
#objects:对象函数
#bools:逻辑函数
#arrays:数组函数
#lists:列表函数
1.使用功能函数
th:value = "${movie.createDate}?$(#dates.format(movie.createDate,'yyyy-MM-dd HH:mm:ss')}:''"<!--#dates.format是一个日期格式化的使用实例,它将电影的创建日期格式化为中文环境的使用格式"yyyy-MM-dd HH:mm:ss-->
2.使用编程语句
Thymeleaf有条件语句、选择语句、循环语句等。
<select name = "actorid" id = "actorid">
<option value = "">选择演员</option>
<option th:each = $actor:${actors}"
th:value = "${actor.id}"
th:text = "${actor.name}">
</option>
</select>
即在下拉列表框中使用循环语句来显示所有的演员列表
3.使用页面框架模板
Thymeleaf的页面框架模板是比较优秀的功能。预先定义一个layout,它具有页眉、页脚、提示栏、导航栏和内容显示等区域。
layout:fragment = “prompt” 提示栏,它可以让引用的视图替换显示的内容
fragments/nav::nav 是一个导航栏并指定了视图文件,也就是说它不能被引用视图替换内容
layout:fragment = “content” 是一个主要内容显示区域,它不能被引用的视图替换显示内容
/fragments/footer::footer 是一个页脚定义并且也指定了视图文件,即不被引用的视图替换显示内容。
3.4 视图设计
视图设计包括列表视图、新建视图、查看视图、修改视图和删除视图设计等5个方面有关数据的增删查改的内容。
视图上的数据存取不是直接与模型打交道,而是通过控制器来处理。
在视图中对于控制器的请求,大多使用JQuery的方式来实现。
3.4.1 列表视图设计
3.5 运行与发布
Web应用启动主程序
WebuiApp.java
@SpringBootApplication
@ComponentScan(basePackages = "com.test")
public class WebuiApp {
public static void main(String[] args) {
SpringApplication.run(WebuiApp.class, args);
}
}
这里要明白一点:
默认第一个打开的文件时static文件夹下的index.html