Spring+SpringMVC+Hibernate實(shí)現(xiàn)投票/調(diào)查問卷網(wǎng)站

原文鏈接:https://zhang35.coding.me/2018-java-web-2-11.html

使用SSH架構(gòu)(Spring+SpringMVC+Hibernate)實(shí)現(xiàn)了簡(jiǎn)單的調(diào)查問卷網(wǎng)站诺苹。最終效果如圖:


投票頁面

查看結(jié)果頁面

下面整理實(shí)現(xiàn)流程啦辐。


前言

1.SSH架構(gòu)

SSH是MVC架構(gòu)的一種實(shí)現(xiàn)。

Spring艳狐、SpringMVC、Hibernate各自用處分別是:

  • Hibernate方便了對(duì)數(shù)據(jù)庫的操作皿桑。一個(gè)對(duì)象映射一個(gè)表毫目,省去了寫SQL語句的繁瑣蔬啡,完成數(shù)據(jù)持久化的任務(wù)。
  • Spring方便了對(duì)象的創(chuàng)建和相互關(guān)聯(lián)镀虐。比如網(wǎng)站啟動(dòng)時(shí)想要初始化的一些對(duì)象箱蟆,可交給Spring管理。
  • SpringMVC實(shí)現(xiàn)了MVC架構(gòu)刮便,使得結(jié)構(gòu)清晰空猜、分工明確。

(Spring和SpringMVC區(qū)別:Spring是IOC和AOP的容器框架恨旱,參考:談?wù)凷pring中的IOC和AOP概念)辈毯;SpringMVC是基于Spring實(shí)現(xiàn)的MVC Web框架)。

2.Maven

Maven是一個(gè)項(xiàng)目管理工具搜贤,有一套標(biāo)準(zhǔn)的工程結(jié)構(gòu)谆沃。其核心配置文件是pom.xml,描述了項(xiàng)目信息仪芒,依賴關(guān)系等唁影。

由于Java項(xiàng)目中需要引入各種jar包,還存在版本差異桌硫,把這些依賴關(guān)系在pom.xml里面描述夭咬,maven就會(huì)自動(dòng)從本地或遠(yuǎn)程倉庫尋找依賴,不用再去一個(gè)個(gè)下載铆隘、拷貝jar包了卓舵。

例如,想引入springmvc框架膀钠,就在pom.xml中加入如下配置:

      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>${spring.version}</version>
      </dependency

3.代碼結(jié)構(gòu)

Java源碼包含Model掏湾、DAO、Service肿嘲、Controller四個(gè)包融击,其中:

  • Model:存放數(shù)據(jù)模型
  • DAO:實(shí)現(xiàn)直接操作Model的接口及方法,比如實(shí)現(xiàn)getPerson()
  • Service:使用DAO提供的接口雳窟,實(shí)現(xiàn)項(xiàng)目需要用到的功能尊浪,比如實(shí)現(xiàn)getAllPersons()
  • Controller:使用Service提供的功能,實(shí)現(xiàn)數(shù)據(jù)分發(fā)及頁面展示封救。

工程結(jié)構(gòu)如下:

工程結(jié)構(gòu)

項(xiàng)目源碼:https://github.com/zhang35/QuizWeb.git


開發(fā)環(huán)境

集成開發(fā)環(huán)境(IDE):IntelliJ IDEA 2017.3.2
本地服務(wù)器:Tomcat 9.0.2
數(shù)據(jù)庫: MySQL 5.7
項(xiàng)目管理:Maven
操作系統(tǒng):MacOS


開發(fā)步驟

1. 準(zhǔn)備工作

2. 搭建SSH項(xiàng)目

3. 實(shí)現(xiàn)投票功能

從操作流程出發(fā),實(shí)現(xiàn)思路是:

3.1 輸入網(wǎng)址進(jìn)入投票界面惩坑。(SpringMVC控制網(wǎng)址->頁面的映射)

3.1.1 在web.xml中配置SpringMVC使之生效掉盅,web.xml:

   <servlet>
        <servlet-name>spring-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

3.1.2 在SpringMVC的配置文件servletname-servlet.xml中配置地址過濾規(guī)則也拜,spring-dispatcher-servlet.xml:

    <context:component-scan base-package="web.quiz.controller" /><!-- 要掃描的包-->
    <mvc:annotation-driven />
    <!-- 解析網(wǎng)址,加前綴后綴趾痘,比如輸入index時(shí)定位到/WEB-INF/jsp/index.jsp-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 不攔截靜態(tài)資源慢哈,保證對(duì)js、css扼脐、jpg等的正常訪問 -->
    <mvc:default-servlet-handler />

3.1.3 最后岸军,在Controller中控制頁面分發(fā),QuizController.java:

    @RequestMapping(value = "vote", method = RequestMethod.GET)
    public String index() {
        return "vote";
    }

3.2 獲取問卷內(nèi)容瓦侮。(Hibernate+Spring從數(shù)據(jù)庫中讀取問卷信息艰赞,發(fā)送到前端頁面)

3.2.1 首先使用Hibernate關(guān)聯(lián)對(duì)象與數(shù)據(jù)庫。Hibernate有配置xml和注解兩種實(shí)現(xiàn)方式肚吏,本文使用注解方妖。Person.java:

//@javax.persistence.Entity注解一個(gè)實(shí)體Bean。數(shù)據(jù)表中的行對(duì)應(yīng)實(shí)例罚攀,列對(duì)應(yīng)實(shí)例的屬性
    @Entity
    public class Person {
        @Id   //必須使用 @javax.persistence.Id 注解一個(gè)主鍵党觅;
        private String id;             //編號(hào)
        private String name;            //姓名
    }

3.2.2 然后實(shí)現(xiàn)相應(yīng)的DAO接口。PersonDAO.java斋泄、PersonDAOImpl.java:

    public interface PersonDAO {
        public List<Person> getAll();
    }
  
    @Repository //@Repository用于標(biāo)注數(shù)據(jù)訪問組件杯瞻,即DAO組件;
    public class PersonDAOImpl implements PersonDAO{
    @Autowired //@Autowired可以對(duì)成員變量炫掐、方法和構(gòu)造函數(shù)進(jìn)行標(biāo)注魁莉,來完成自動(dòng)裝配。默認(rèn)按照類型進(jìn)行裝配募胃。
    private SessionFactory sessionFactory;
      public List<Person> getAll() {
          Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Person.class);
          return criteria.list();
      }
  }

3.2.3 PersonDAOImpl中要用的sessionFactory怎么來旗唁?什么是SessionFactory:

SessionFactory接口負(fù)責(zé)初始化Hibernate。SessionFactory并不是輕量級(jí)的痹束,因?yàn)橐话闱闆r下检疫,一個(gè)項(xiàng)目通常只需要一個(gè)SessionFactory就夠。

所以把SessionFactory放在Web初始化時(shí)候生成祷嘶,使用Spring實(shí)現(xiàn)其自動(dòng)裝配屎媳。

首先在web.xml中配置Spring:

    <!--配置Spring-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/META-INF/applicationContext.xml,
            classpath:/META-INF/spring-jdbc.xml
        </param-value>
    </context-param>

然后在Spring配置文件中配置Hibernate,spring-jdbc.xml:

       <!--自定義的hibernate.properties文件,下面${XXX}的內(nèi)容來源-->
       <context:property-placeholder location="classpath:/META-INF/properties/hibernate.properties" />

       <!-- 使用C3P0數(shù)據(jù)源论巍,MySQL數(shù)據(jù)庫 -->
       <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
             destroy-method="close">
              <!-- MySQL5 -->
              <property name="driverClass" value="${driverClassName}"></property>
              <property name="jdbcUrl" value="${url}"></property>
              <property name="user" value="${username}"></property>
              <property name="password" value="${password}"></property>
       </bean
       <!-- session工廠 -->
       <bean id="sessionFactory"
             class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
              <property name="dataSource" ref="dataSource" />
              <property name="packagesToScan" value="web.quiz.model" />
              <property name="hibernateProperties">
                     <props>
                            <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
                            <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                            <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                            <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                     </props>
              </property>
       </bean>
        <!--hibernate的事務(wù)管理器-->
       <bean id="transactionManager"
             class="org.springframework.orm.hibernate4.HibernateTransactionManager">
              <property name="sessionFactory" ref="sessionFactory"></property>
       </bean>

在Spring的默認(rèn)配置文件applicationContext.xml中配置掃描的包剿牺。這些包中有Spring注解為@Component的類,在使用注解配置的情況下环壤,系統(tǒng)啟動(dòng)時(shí)會(huì)被自動(dòng)掃描,并添加到bean工廠中去(省去了配置文件中寫bean定義了)钞诡。applicationContext.xml:

    <context:component-scan base-package="web.quiz.service"/>
    <context:component-scan base-package="web.quiz.DAO"/>

至此郑现,PersonDAOImpl中有了可用的sessionFactory湃崩,它的功能也能盡數(shù)實(shí)現(xiàn)了,如3.2.2中所示接箫。

3.2.4 此時(shí)有了PersonDAO的實(shí)現(xiàn)攒读,進(jìn)一步封裝成可直接用的服務(wù)。DBService.java 辛友、DBServiceImpl.java:

public interface DBService{
    public List<Person> loadPersons();
}

@Service  //@Service用于標(biāo)注業(yè)務(wù)層組件
@Transactional //@Transactional 可以作用于接口薄扁、接口方法、類以及類方法上废累。賦予其事務(wù)屬性
public class DBServiceImpl implements DBService{
    @Autowired
    private PersonDAO personDAO;
    public List<Person> loadPersons(){
        return personDAO.getAll();
    }
}

至此邓梅,讀寫數(shù)據(jù)的功能有了∫乇酰可以在Controller中調(diào)用服務(wù)讀數(shù)據(jù)日缨,QuizController.java:

@Controller
public class QuizController {
    @Resource  //與@Autowired等效,是JDK支持的注解掖看,默認(rèn)按照名稱進(jìn)行裝配
    private DBService dbService;
    @PostConstruct  //使用@PostConstruct注釋初始化方法匣距。在Controller中,用@PostConstruct修飾的方法會(huì)在服務(wù)器加載Servlet的時(shí)候運(yùn)行哎壳,并且只會(huì)被服務(wù)器調(diào)用一次毅待。
    private void initQuiz(){
        this.persons = dbService.loadPersons();
    }
}

3.2.5 把讀取的內(nèi)容發(fā)給前端JSP頁面。本文使用了兩種方法归榕,一是傳json回去尸红,二是直接傳對(duì)象回去。

方法一:傳json到前端蹲坷,用拼接字符串的方式生成頁面驶乾。這種方法很笨,當(dāng)時(shí)知識(shí)面太窄循签。不過用json傳數(shù)據(jù)在做數(shù)據(jù)可視化時(shí)比較方便级乐,所以也保留了這些代碼。QuizController.java:

 @RequestMapping(value = "/loadPaper", method = RequestMethod.GET)
    @ResponseBody
    public byte[] loadPaper() throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonString = objectMapper.writeValueAsString(this.quiz);
        byte[] b = jsonString.getBytes("UTF-8");        //解決傳到前端后中文亂碼問題
        return b;
    }

在前端用js解析json县匠,生成頁面风科。問卷頁面vote.jsp:

function loadPage(){
            $.getJSON("loadPaper",function(data){  //獲取問卷數(shù)據(jù)quiz,放入data中
                var names = data.names;
                 for (var i=0; i< names.length; i++){
                      var testDiv = '<div class="test">' + '<p class="name">' + (i+1) + ".&nbsp" + names[i] + ':</p>';
                      ...
                      $("form").append(testDiv);
                }
             }

方法二:借助ModelAndView對(duì)象,直接傳對(duì)象到前端乞旦。配合JSP的特點(diǎn)贼穆,比較優(yōu)雅高效。QuizController.java中:

//在方法的參數(shù)列表中添加形參 ModelMap map, spring 會(huì)自動(dòng)創(chuàng)建ModelMap對(duì)象兰粉。
//然后調(diào)用map的put(key,value)或者addAttribute(key,value)將數(shù)據(jù)放入map中故痊,spring會(huì)自動(dòng)將數(shù)據(jù)存入request。
public ModelAndView check(HttpServletRequest request, HttpServletResponse response, ModelMap model){
  model.addAttribute("persons", persons);  //添加名為persons的對(duì)象
            return new ModelAndView("result");   //返回頁面result.jsp
}

在前端使用JSTL <c:forEach>和EL表達(dá)式循環(huán)生成表格玖姑。顯示成績(jī)頁面result.jsp:

        <c:forEach items="${persons}" var="person">
            <tr>
                <td>${person.id}</td>
                <td><a href="${person.id}/detail">${person.name}</a></td> <!--REST風(fēng)格-->
                <td>${person.department}</td>
            </tr>
        </c:forEach>

(注意:使用EL表達(dá)式時(shí)愕秫,需加入<%@page isELIgnored="false" %>)

這里把每個(gè)人名作為一個(gè)超鏈接慨菱,點(diǎn)進(jìn)去顯示其測(cè)評(píng)結(jié)果。${person.id}/detail會(huì)變成如1/detail這樣的地址戴甩。該地址在QuizController.java中這么解析:

@RequestMapping(value = "/{id}/detail", method = RequestMethod.GET)
    public ModelAndView detail(HttpServletRequest request, ModelMap model, @PathVariable String id) {
        Result result = dbService.getResultByID(id);
        String name = result.getName();
        model.addAttribute("name", name);
        return new ModelAndView("detail");
    }

3.2.6 擴(kuò)展知識(shí)
上述傳參方式叫REST( Representational State Transfer)風(fēng)格符喝。
關(guān)于SpringMVC應(yīng)用REST風(fēng)格,參考:Spring MVC 實(shí)現(xiàn)增刪改查 - CSDN博客
關(guān)于其它URL傳參方式甜孤,參考: SpringMVC之@RequestParam @PathVariable對(duì)比
關(guān)于SpringMVC中各種常用傳值方法协饲,參考:springMVC 將controller中數(shù)據(jù)傳遞到j(luò)sp頁面

3.3 填寫問卷,點(diǎn)提交缴川。(Controller接收前端表單茉稠,結(jié)果寫入數(shù)據(jù)庫)

主要是前端傳值到后端的問題。
在vote.jsp中:

<form method="POST" action="submit">
...
</form>

在QuizController.java中:

 @RequestMapping(value = "/submit", method = RequestMethod.POST)
    public String submit(HttpServletRequest request, HttpServletResponse response) {
        Enumeration<String> enu = request.getParameterNames();        //獲得表單中所有值
        while (enu.hasMoreElements()) {
            String paraName = (String) enu.nextElement();
            String value = request.getParameter(paraName);   //按照表單中的順序二跋,一個(gè)個(gè)接收值
            ...
            dbService.saveOrUpdateResult(result); //將成績(jī)寫入數(shù)據(jù)庫中
        }
    }

3.4 管理員登錄战惊,查看結(jié)果。(身份驗(yàn)證后扎即,JSP展示結(jié)果)

提供管理員登錄頁面login.jsp吞获,登錄后進(jìn)入?yún)⒃u(píng)人列表頁面result.jsp;點(diǎn)擊參評(píng)人超鏈接谚鄙,進(jìn)入詳細(xì)成績(jī)頁面detail.jsp各拷,統(tǒng)計(jì)分析個(gè)人得票結(jié)果。

到這里已經(jīng)沒有什么難度闷营,不再寫了烤黍。


一點(diǎn)心得

1. hibernate如何將數(shù)組成員對(duì)應(yīng)到數(shù)據(jù)庫

對(duì)于Question對(duì)象,成員id傻盟、title都能自動(dòng)對(duì)應(yīng)數(shù)據(jù)庫中表的一列速蕊,而options作為一個(gè)List就帶來很多麻煩。

解決方法:把List<String>拼接成一個(gè)String保存娘赴,當(dāng)使用時(shí)规哲,按照約定規(guī)則拆分即可:

    String[] options = questions[i].options.split("#"); //自行約定,這里用#分隔字符

成員變?yōu)椋?/p>

    private String options;

完美诽表!


結(jié)語

終于搭好Java SSH這個(gè)架子唉锌,實(shí)現(xiàn)了基本功能,但不想再繼續(xù)寫了竿奏。Web技術(shù)迭代太快袄简,后面去嘗試下其它技術(shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末泛啸,一起剝皮案震驚了整個(gè)濱河市绿语,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖汞舱,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伍纫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡昂芜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門赔蒲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泌神,“玉大人,你說我怎么就攤上這事舞虱』都剩” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵矾兜,是天一觀的道長(zhǎng)损趋。 經(jīng)常有香客問我,道長(zhǎng)椅寺,這世上最難降的妖魔是什么浑槽? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮返帕,結(jié)果婚禮上桐玻,老公的妹妹穿的比我還像新娘。我一直安慰自己荆萤,他們只是感情好镊靴,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著链韭,像睡著了一般偏竟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敞峭,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天踊谋,我揣著相機(jī)與錄音,去河邊找鬼儡陨。 笑死褪子,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的骗村。 我是一名探鬼主播嫌褪,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼胚股!你這毒婦竟也來了笼痛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缨伊,沒想到半個(gè)月后摘刑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡刻坊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年枷恕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谭胚。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡徐块,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出灾而,到底是詐尸還是另有隱情胡控,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布旁趟,位于F島的核電站昼激,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏锡搜。R本人自食惡果不足惜橙困,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望余爆。 院中可真熱鬧纷宇,春花似錦、人聲如沸蛾方。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桩砰。三九已至拓春,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間亚隅,已是汗流浹背硼莽。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留煮纵,地道東北人懂鸵。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像行疏,于是被迫代替她去往敵國和親匆光。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,846評(píng)論 6 342
  • 1美澳、談?wù)勀銓?duì)Struts的理解。 答: 1.struts是一個(gè)按MVC模式設(shè)計(jì)的Web層框架磨取,其實(shí)它就是一個(gè)大大的...
    慕容小偉閱讀 2,805評(píng)論 0 13
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理人柿,服務(wù)發(fā)現(xiàn),斷路器忙厌,智...
    卡卡羅2017閱讀 134,696評(píng)論 18 139
  • 人的這一路,終究是要自己走的江咳,無論是開心逢净,快樂,痛苦歼指,悲哀爹土,絕望,失望踩身,哭泣胀茵,最終都是要自己承擔(dān)的,或許你開心了可...
    墜金釵520閱讀 251評(píng)論 0 0
  • 習(xí)慣了每周假期只有半天 習(xí)慣了每天熬夜熬到十一點(diǎn) 習(xí)慣了在題海里拼命奮戰(zhàn) 習(xí)慣了做題作到自己狼狽不堪 只為了那一串...
    鳴兮閱讀 257評(píng)論 4 8