Springboot深入实践-3.Springboot界面设计

本文深入探讨了使用SpringBoot框架结合Neo4j数据库进行MVC架构设计的过程,包括模型、视图、控制器的设计细节,以及Thymeleaf模板的使用和分页查询的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chenglin_Yu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值