Spring Data,MongoDB和JSF集成教程
示例应用程序简介(MongoShop产品目录)
在学习完本教程之后,将构建具有以下功能要求的示例应用程序(MongoShop产品目录):
1.搜索具有不同条件的产品(例如,sku,产品类型,标题,stc)
2.创建具有不同类别的新产品。
3.编辑选定的产品详细信息
4.从查询屏幕删除选定的产品。
表示层:
JSF在此示例应用程序中用作表示层技术。 PrimeFaces是用于增强JSF UI的轻量级组件之一。 前端交互由该层中的JSF支持bean控制。
服务层:
使用Spring管理的单例服务对象。 业务服务和应用程序逻辑编写在此层中
数据层:
使用Spring数据MongoDB组件。 它提供了与MongoDB面向文档的数据库的集成。 它提供了MongoTemplate,以便可以轻松执行MongoDB操作。 而且,Spring存储库样式数据访问层可以使用Spring数据MongoDB轻松编写。
MongoDB模式设计和数据准备
MongoDB简介
MongoDB是一个开源可扩展的高性能NoSQL数据库。 它是一个面向文档的存储。 它可以存储具有动态模式的JSON样式的文档。 在此应用程序中,每个产品都以JSON样式的文档存储在MongoDB中。
MongoDB中的架构设计
目录中的每个产品均包含常规产品信息(例如,sku,标题和产品类型),价格详细信息(例如,零售价格和标价)和产品子详细信息(例如,音频CD的曲目/书籍的章节)。 在此应用程序中,使用MongoDB。 模式设计将更侧重于数据使用。 它与传统的RDBMS模式设计不同。 MongoDB中的架构设计应为:
样本数据:
x= { sku: '1000001', type: 'Audio Album', title: 'A Love Supreme', description: 'by John Coltrane', publisher: 'Sony Music', pricing: { list: 1200, retail: 1100 }, details: { title: 'A Love Supreme [Original Recording Reissued]', artist: 'John Coltrane', genre: 'Jazz' , tracks: [ 'A Love Supreme Part I: Acknowledgement', 'A Love Supreme Part II - Resolution', 'A Love Supreme, Part III: Pursuance', 'A Love Supreme, Part IV-Psalm' ], } } y= { sku: '1000002', type: 'Audio Album', title: 'Love Song', description: 'by Khali Fong', publisher: 'Sony Music', pricing: { list: 1000, retail: 1200 }, details: { title: 'Long Song [Original Recording Reissued]', artist: 'Khali Fong', genre: 'R&B', tracks: [ 'Love Song', 'Spring Wind Blow', 'Red Bean', 'SingAlongSong' ], } } z= { sku: '1000003', type: 'Book', title: 'Node.js for PHP Developers', description: 'by Owen Peter', publisher: 'OReilly Media', pricing: { list: 2500, retail: 2100 }, details: { title: 'Node.js for PHP Developers', author: 'Mark Owen', genre: 'Technology', chapters: [ 'Introduction to Node', 'Server-side JS', 'PHP API', 'Example' ], } }
示例查询以添加数据:
db.product.save(x); db.product.save(y); db.product.save(z);
示例查询以测试示例数据:
db.product.find({'sku':'1000004'}); db.product.find({'type':'Audio Album'}); db.product.find({'type':'Audio Album', 'details.genre': 'Jazz'});
JSF(PrimeFaces)和Spring数据MongoDB集成
项目的pom.xml
<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/maven-v4_0_0.xsd'> <modelversion>4.0.0</modelVersion> <groupid>com.borislam</groupId> <artifactid>mongoShop</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>MongoShop Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupid>org.jboss.el</groupId> <artifactid>com.springsource.org.jboss.el</artifactId> <version>2.0.0.GA</version> </dependency> <dependency> <groupid>org.primefaces.themes</groupId> <artifactid>all-themes</artifactId> <version>1.0.9</version> </dependency> <dependency> <groupid>org.primefaces</groupId> <artifactid>primefaces</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupid>commons-beanutils</groupId> <artifactid>commons-beanutils</artifactId> <version>1.8.3</version> </dependency> <dependency> <groupid>commons-codec</groupId> <artifactid>commons-codec</artifactId> <version>1.3</version> </dependency> <dependency> <groupid>org.apache.directory.studio</groupId> <artifactid>org.apache.commons.lang</artifactId> <version>2.6</version> </dependency> <dependency> <groupid>commons-digester</groupId> <artifactid>commons-digester</artifactId> <version>1.8</version> </dependency> <dependency> <groupid>commons-collections</groupId> <artifactid>commons-collections</artifactId> <version>3.2</version> </dependency> <dependency> <groupid>org.apache.myfaces.core</groupId> <artifactid>myfaces-api</artifactId> <version>2.1.9</version> </dependency> <dependency> <groupid>org.apache.myfaces.core</groupId> <artifactid>myfaces-impl</artifactId> <version>2.1.9</version> </dependency> <dependency> <groupid>org.mongodb</groupId> <artifactid>mongo-java-driver</artifactId> <version>2.10.1</version> </dependency> <dependency> <groupid>junit</groupId> <artifactid>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupid>org.springframework.data</groupId> <artifactid>spring-data-mongodb</artifactId> <version>1.0.3.RELEASE</version> </dependency> <dependency> <groupid>org.springframework</groupId> <artifactid>spring-context</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupid>org.springframework</groupId> <artifactid>spring-web</artifactId> <version>3.2.0.RELEASE</version> </dependency> </dependencies> <repositories> <repository> <id>java.net</id> <url>https://maven.java.net/content/repositories/public/</url> </repository> <repository> <id>prime-repo</id> <name>PrimeFaces Maven Repository</name> <url>http://repository.primefaces.org</url> <layout>default</layout> </repository> <repository> <id>com.springsource.repository.bundles.release</id> <name>SpringSource Enterprise Bundle Repository - SpringSource Releases</name> <url>http://repository.springsource.com/maven/bundles/release</url> </repository> <repository> <id>com.springsource.repository.bundles.external</id> <name>SpringSource Enterprise Bundle Repository - External Releases</name> <url>http://repository.springsource.com/maven/bundles/external</url> </repository> <repository> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> <id>apache.snapshots</id> <name>Apache Snapshot Repository</name> <url>https://repository.apache.org/content/repositories/snapshots</url> </repository> <repository> <id>jboss-deprecated-repository</id> <name>JBoss Deprecated Maven Repository</name> <url>https://repository.jboss.org/nexus/content/repositories/deprecated/</url> <layout>default</layout> <releases> <enabled>true</enabled> <updatepolicy>never</updatePolicy> </releases> <snapshots> <enabled>false</enabled> <updatepolicy>never</updatePolicy> </snapshots> </repository> </repositories> <build> <finalname>mongoShop</finalName> </build> </project>
我的脸
MyFaces用作此应用程序中的JSF实现。 以下详细信息应添加到web.xml中
PrimeFaces主题
如前所述,PrimeFaces库用于增强UI。 该库几乎不需要配置。 PrimeFaces为您的Web应用程序提供了许多预先设计的主题。 在本例中,我们使用“蓝天”主题。 我们只是在web.xml中添加以下设置
<context-param> <param-name>primefaces.THEME</param-name><param-value>glass-x</param-value></context-param>
JSF和Spring集成:
要将JSF与Spring集成,必须在Faces-config.xml中指定SpringBeanFacesELResolver
Faces-config.xml
<?xml version='1.0' encoding='UTF-8'?> <faces-config version='2.0' xmlns='http://java.sun.com/xml/ns/javaee' xmlns:xi='http://www.w3.org/2001/XInclude' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd'> <application> <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver> </application> <factory> <partial-view-context-factory>org.primefaces.context.PrimePartialViewContextFactory</partial-view-context-factory> </factory> </faces-config>
完整的web.xml
<?xml version='1.0' encoding='UTF-8'?> <web-app id='WebApp_ID' version='3.0' xmlns='http://java.sun.com/xml/ns/javaee' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd'> <context-param> <param-name>contextConfigLocation</param-name><param-value>WEB-INF/spring-application-context.xml</param-value></context-param> <context-param> <param-name>errorPageUrl</param-name><param-value>/pages/systemError.do</param-value></context-param> <context-param> <param-name>facelets.DEVELOPMENT</param-name><param-value>false</param-value></context-param> <context-param> <param-name>facelets.REFRESH_PERIOD</param-name><param-value>2</param-value></context-param> <context-param> <param-name>javax.faces.STATE_SAVING_METHOD</param-name><param-value>client</param-value></context-param> <context-param> <param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name><param-value>resources.application</param-value></context-param> <context-param> <param-name>org.apache.myfaces.ALLOW_JAVASCRIPT</param-name><param-value>true</param-value></context-param> <context-param> <param-name>org.apache.myfaces.AUTO_SCROLL</param-name><param-value>false</param-value></context-param> <context-param> <param-name>org.apache.myfaces.DETECT_JAVASCRIPT</param-name><param-value>false</param-value></context-param> <context-param> <param-name>org.apache.myfaces.ERROR_HANDLING</param-name><param-value>false</param-value></context-param> <context-param> <param-name>org.apache.myfaces.EXPRESSION_FACTORY</param-name><param-value>org.jboss.el.ExpressionFactoryImpl</param-value></context-param> <context-param> <param-name>org.apache.myfaces.PRETTY_HTML</param-name><param-value>false</param-value></context-param> <context-param> <param-name>primefaces.THEME</param-name><param-value>glass-x</param-value></context-param> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>org.apache.myfaces.webapp.MyFacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> <listener> <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
MongoDB连接详细信息
为了连接到MongoDB,您必须以XML注册MongoDbFactory实例。 连接详细信息在spring-application-context.xml中指定
spring-application-context.xml
<?xml version='1.0' encoding='UTF-8'?> <beans xmlns='http://www.springframework.org/schema/beans' xmlns:context='http://www.springframework.org/schema/context' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:util='http://www.springframework.org/schema/util' xmlns:mongo='http://www.springframework.org/schema/data/mongo' xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd'> <context:annotation-config/> <context:component-scan base-package='com.borislam'/> <mongo:mongo host='localhost' port='27017'> <mongo:options connections-per-host='5' connect-timeout='30000' max-wait-time='10000' write-number='1' write-timeout='0' write-fsync='true'/> </mongo:mongo> <mongo:db-factory dbname='test' mongo-ref='mongo'/> <mongo:repositories base-package='com.borislam.repository' /> <bean id='mongoTemplate' class='org.springframework.data.mongodb.core.MongoTemplate'> <constructor-arg ref='mongo'/><constructor-arg name='databaseName' value='test'/></bean> </beans>
使用Spring Data Repository和MongoTemplate来查询数据
Spring资料储存库:
Spring数据存储库抽象减少了用于编写应用程序数据访问层的样板代码。 Repository接口的自动实现提供了对mongoDB的简单操作。 它有助于我们的产品保存和删除功能,使
MongoTemplate:
MongoTemplate提供了方便的操作来创建,更新,删除和查询MongoDB文档,并提供了域对象和MongoDB文档之间的映射。 在我们的应用程序中,由于spring数据存储库无法满足搜索功能的要求,因此我们使用MongoTemplate来归档搜索功能。
自定义Spring数据存储库:
由于无法通过Spring数据存储库抽象轻松地实现产品搜索,因此我们想使用MongoDBTemplate实现多级Criteira产品搜索。 为了使用MongoTemplate丰富Spring数据存储库,我们可以执行以下操作来定制存储库:
ProductRepository.java
package com.borislam.repository; import java.util.List; import org.springframework.data.repository.PagingAndSortingRepository; import com.borislam.domain.Product; public interface ProductRepository extends PagingAndSortingRepository<Product, String> , ProductRepostitoryCustom{ List<product> findByType(String type); List<product> findByTypeAndTitle(String type, String title); Product findBySku(String sku); }
ProductRepositoryCustom.java
package com.borislam.repository; import java.util.List; import com.borislam.domain.Product; import com.borislam.view.ProductSearchCriteria; public interface ProductRepostitoryCustom { public List<product> searchByCriteria(ProductSearchCriteria criteria); }
ProductRepositoryImpl.java
package com.borislam.repository.impl; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.util.StringUtils; import com.borislam.domain.Product; import com.borislam.repository.ProductRepostitoryCustom; import com.borislam.view.ProductSearchCriteria; public class ProductRepositoryImpl implements ProductRepostitoryCustom{ @Autowired private MongoTemplate mongoTemplate; @Override public List<product> searchByCriteria(ProductSearchCriteria criteria) { Query query = new Query(); if ( StringUtils.hasText(criteria.getSku())) { Criteria c = Criteria.where('sku').is(criteria.getSku()); query.addCriteria(c); } if (StringUtils.hasText(criteria.getTitle())) { Criteria c = Criteria.where('title').regex('.*' + criteria.getTitle() + '.*', 'i'); query.addCriteria(c); } if (StringUtils.hasText(criteria.getDescription())) { Criteria c = Criteria.where('description').regex('.*' + criteria.getDescription() + '.*', 'i'); query.addCriteria(c); } if (StringUtils.hasText(criteria.getProductType())) { Criteria c = Criteria.where('type').is(criteria.getProductType()); query.addCriteria(c); } if (StringUtils.hasText(criteria.getTrack())) { Criteria c = Criteria.where('details.tracks').regex('.*' + criteria.getTrack() + '.*', 'i'); query.addCriteria(c); } if (StringUtils.hasText(criteria.getChapter())) { Criteria c = Criteria.where('details.chapters').regex('.*' + criteria.getChapter() + '.*', 'i'); query.addCriteria(c); } return mongoTemplate.find(query, Product.class); } }
数据模型:Product.java
package com.borislam.domain; public class Product { private String id; private String sku ; private String type; private String title; private String description; private String publisher; private Pricing pricing; private Detail details; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getSku() { return sku; } public void setSku(String sku) { this.sku = sku; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getPublisher() { return publisher; } public void setPublisher(String publisher) { this.publisher = publisher; } public Pricing getPricing() { return pricing; } public void setPricing(Pricing pricing) { this.pricing = pricing; } public Detail getDetails() { return details; } public void setDetails(Detail details) { this.details = details; } }
Pricing.java
package com.borislam.domain; public class Pricing { private String id; private double list; private double retail; public String getId() { return id; } public void setId(String id) { this.id = id; } public double getList() { return list; } public void setList(double list) { this.list = list; } public double getRetail() { return retail; } public void setRetail(double retail) { this.retail = retail; } public Pricing(double list, double retail) { super(); this.list = list; this.retail = retail; } }
Detail.java
package com.borislam.domain; import java.util.List; public class Detail { private String id; private String title; private String author; private String artist; private String genre; private List<string> pic; private List<string> chapters; private List<string> tracks; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getGenre() { return genre; } public void setGenre(String genre) { this.genre = genre; } public List<string> getPic() { return pic; } public void setPic(List<string> pic) { this.pic = pic; } public List<string> getChapters() { return chapters; } public void setChapters(List<string> chapters) { this.chapters = chapters; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getArtist() { return artist; } public void setArtist(String artist) { this.artist = artist; } public List<string> getTracks() { return tracks; } public void setTracks(List<string> tracks) { this.tracks = tracks; } }
JSF部分:common.xhtml
<html xmlns='http://www.w3.org/1999/xhtml' xmlns:h='http://java.sun.com/jsf/html' xmlns:f='http://java.sun.com/jsf/core' xmlns:ui='http://java.sun.com/jsf/facelets' xmlns:p='http://primefaces.org/ui'> <f:view contentType='text/html'> <h:head> <f:facet name='first'> <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/> <title><ui:insert name='pageTitle'>Page Title</ui:insert></title> <ui:insert name='head' /> </f:facet> </h:head> <h:body> <div style='margin:auto;width:1024px;'> <div id='header' class='ui-widget' > <div id='logo' style='border:1px solid #acbece; border-bottom: none; '> <p:graphicImage value='/resources/image/mongoshopheader.jpg'/></div> <div id='logo' style='border:1px solid #acbece;'> <p:menubar style='border:none'><p:menuitem value='Search' url='/search.jsf' icon='ui-icon-search' /><p:menuitem value='New Product' url='/detail.jsf' icon='ui-icon-document' /></p:menubar></div> </div> <div id='page' class='ui-widget' style='overflow:hidden;'> <div id='content' style='display:block'> <ui:insert name='content'>...</ui:insert> </div> </div> </div> </h:body> </f:view> </html>
Search.xhml
<html xmlns='http://www.w3.org/1999/xhtml' xmlns:ui='http://java.sun.com/jsf/facelets' xmlns:h='http://java.sun.com/jsf/html' xmlns:f='http://java.sun.com/jsf/core' xmlns:p='http://primefaces.org/ui'> <ui:composition template='/template/common.xhtml'> <ui:define name='pageTitle'> <h:outputText value='Product Search' /> </ui:define> <ui:define name='content'> <h:form id='searchForm'> <p:growl id='mainGrowl' sticky='true' /><p:panelGrid style='width:1024px'><f:facet name='header'> <p:row><p:column colspan='4'>Product Search </p:column></p:row></f:facet> <p:row><p:column><h:outputLabel for='sku' value='sku: ' /> </p:column><p:column><p:inputText id='sku' value='#{productSearchBean.criteria.sku}' /></p:column><p:column><h:outputLabel for='productType' value='Product Type: ' /> </p:column><p:column><p:selectOneMenu id='productType' label='Type' value='#{productSearchBean.criteria.productType}' ><f:selectItem itemLabel='Select One' itemValue='' /> <f:selectItem itemLabel='Audio Album' itemValue='Audio Album' /> <f:selectItem itemLabel='Book' itemValue='Book' /> </p:selectOneMenu></p:column></p:row><p:row><p:column><h:outputLabel for='title' value='Title: ' /> </p:column><p:column><p:inputText id='title' value='#{productSearchBean.criteria.title}' /></p:column><p:column><h:outputLabel for='description' value='Description: ' /> </p:column><p:column><p:inputText id='description' value='#{productSearchBean.criteria.description}' /></p:column></p:row><p:row><p:column><h:outputLabel for='track' value='Track: ' /> </p:column><p:column><p:inputText id='track' value='#{productSearchBean.criteria.track}' /></p:column><p:column><h:outputLabel for='chapter' value='Chapter: ' /> </p:column><p:column><p:inputText id='chapter' value='#{productSearchBean.criteria.chapter}' /></p:column></p:row></p:panelGrid><p:commandButton value='search' icon='ui-icon-search' actionListener='#{productSearchBean.doSearch}' update='dataTable'/><hr/><p:dataTable id='dataTable' var='prod' value='#{productSearchBean.productList}' paginator='true' rows='10'><p:column><f:facet name='header'> <h:outputText value='Sku' /> </f:facet> <h:outputText value='#{prod.sku}' /> </p:column><p:column><f:facet name='header'> <h:outputText value='Type' /> </f:facet> <h:outputText value='#{prod.type}' /> </p:column><p:column><f:facet name='header'> <h:outputText value='Title' /> </f:facet> <h:outputText value='#{prod.title}' /> </p:column><p:column><f:facet name='header'> <h:outputText value='Publisher' /> </f:facet> <h:outputText value='#{prod.publisher}' /> </p:column><p:column><f:facet name='header'> <h:outputText value='Artist' /> </f:facet> <h:outputText value='#{prod.details.artist}' /> </p:column><p:column><f:facet name='header'> <h:outputText value='Author' /> </f:facet> <h:outputText value='#{prod.details.author}' /> </p:column></p:dataTable></h:form> </ui:define> </ui:composition> </html>
ProductSearchCriteria.java
package com.borislam.view; public class ProductSearchCriteria { private String sku; private String description; private String productType; private String track; private String chapter; private String title; public String getSku() { return sku; } public void setSku(String sku) { this.sku = sku; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getProductType() { return productType; } public void setProductType(String productType) { this.productType = productType; } public String getTrack() { return track; } public void setTrack(String track) { this.track = track; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getChapter() { return chapter; } public void setChapter(String chapter) { this.chapter = chapter; } }
ProductSearchBean.java
package com.borislam.view; import java.util.List; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Component; import com.borislam.domain.Product; import com.borislam.service.ProductService; @Component @Scope('session') public class ProductSearchBean { private Product selectedProduct; private ProductSearchCriteria criteria = new ProductSearchCriteria(); private List<product> productList; public Product getSelectedProduct() { return selectedProduct; } public void setSelectedProduct(Product selectedProduct) { this.selectedProduct = selectedProduct; } public List<product> getProductList() { return productList; } public void setProductList(List<product> productList) { this.productList = productList; } public ProductSearchCriteria getCriteria() { return criteria; } public void setCriteria(ProductSearchCriteria criteria) { this.criteria = criteria; } @Autowired private ProductService productService; public void doSearch(ActionEvent event){ productList= productService.searchByCriteria(criteria); } }
服务层:ProductService.java
package com.borislam.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.borislam.domain.Product; import com.borislam.repository.ProductRepository; import com.borislam.view.ProductSearchCriteria; @Service public class ProductService { @Autowired private ProductRepository productRepository; public List<product> searchByCriteria(ProductSearchCriteria criteria){ return productRepository.searchByCriteria(criteria); } public Product getProduct(String sku) { return productRepository.findBySku(sku); } }
使用Spring数据存储库创建,编辑和删除数据
在本教程的最后一部分,我们将向MongoShop产品目录应用程序添加创建,编辑和删除功能。 搜索页面已修改。 在实际删除产品之前,已添加模式确认对话框
更新了search.xhtml
<html xmlns='http://www.w3.org/1999/xhtml' xmlns:ui='http://java.sun.com/jsf/facelets' xmlns:h='http://java.sun.com/jsf/html' xmlns:f='http://java.sun.com/jsf/core' xmlns:p='http://primefaces.org/ui'> <ui:composition template='/template/common.xhtml'> <ui:define name='pageTitle'> <h:outputText value='Product Search' /> </ui:define> <ui:define name='content'> <h:form id='searchForm'> <p:growl id='mainGrowl' sticky='true' /> <p:panelGrid style='width:1024px'> <f:facet name='header'> <p:row> <p:column colspan='4'> Product Search </p:column> </p:row> </f:facet> <p:row> <p:column> <h:outputLabel for='sku' value='sku: ' /> </p:column> <p:column> <p:inputText id='sku' value='#{productSearchBean.criteria.sku}' /> </p:column> <p:column> <h:outputLabel for='productType' value='Product Type: ' /> </p:column> <p:column> <p:selectOneMenu id='productType' label='Type' value='#{productSearchBean.criteria.productType}' > <f:selectItem itemLabel='Select One' itemValue='' /> <f:selectItem itemLabel='Audio Album' itemValue='Audio Album' /> <f:selectItem itemLabel='Book' itemValue='Book' /> </p:selectOneMenu> </p:column> </p:row> <p:row> <p:column> <h:outputLabel for='title' value='Title: ' /> </p:column> <p:column> <p:inputText id='title' value='#{productSearchBean.criteria.title}' /> </p:column> <p:column> <h:outputLabel for='description' value='Description: ' /> </p:column> <p:column> <p:inputText id='description' value='#{productSearchBean.criteria.description}' /> </p:column> </p:row> <p:row> <p:column> <h:outputLabel for='track' value='Track: ' /> </p:column> <p:column> <p:inputText id='track' value='#{productSearchBean.criteria.track}' /> </p:column> <p:column> <h:outputLabel for='chapter' value='Chapter: ' /> </p:column> <p:column> <p:inputText id='chapter' value='#{productSearchBean.criteria.chapter}' /> </p:column> </p:row> </p:panelGrid> <p:commandButton value='search' icon='ui-icon-search' actionListener='#{productSearchBean.doSearch}' update='dataTable'/> <hr/> <p:dataTable id='dataTable' var='prod' value='#{productSearchBean.productList}' paginator='true' rows='10'> <p:column> <f:facet name='header'> <h:outputText value='Sku' /> </f:facet> <h:outputText value='#{prod.sku}' /> </p:column> <p:column> <f:facet name='header'> <h:outputText value='Type' /> </f:facet> <h:outputText value='#{prod.type}' /> </p:column> <p:column> <f:facet name='header'> <h:outputText value='Title' /> </f:facet> <h:outputText value='#{prod.title}' /> </p:column> <p:column> <f:facet name='header'> <h:outputText value='Publisher' /> </f:facet> <h:outputText value='#{prod.publisher}' /> </p:column> <p:column> <f:facet name='header'> <h:outputText value='Artist' /> </f:facet> <h:outputText value='#{prod.details.artist}' /> </p:column> <p:column> <f:facet name='header'> <h:outputText value='Author' /> </f:facet> <h:outputText value='#{prod.details.author}' /> </p:column> <p:column> <f:facet name='header'> <h:outputText value='Edit' /> </f:facet> <p:commandButton value='Edit' action='#{productSearchBean.doEditDetail}' ajax='false'> <f:setPropertyActionListener target='#{productSearchBean.selectedProduct}' value='#{prod}' /> </p:commandButton> </p:column> <p:column> <f:facet name='header'> <h:outputText value='Delete' /> </f:facet> <p:commandButton id='showDialogButton' value='Delete' oncomplete='confirmation.show()' ajax='true' update=':searchForm:confirmDialog'> <f:setPropertyActionListener target='#{productSearchBean.selectedProduct}' value='#{prod}' /> </p:commandButton> </p:column> </p:dataTable> <p:confirmDialog id='confirmDialog' message='Are you sure to delete this product (#{productSearchBean.selectedProduct.sku})?' header='Delete Product' severity='alert' widgetVar='confirmation'> <p:commandButton id='confirm' value='Yes' update='mainGrowl' oncomplete='confirmation.hide()' actionListener='#{productSearchBean.doDelete}' /> <p:commandButton id='decline' value='No' οnclick='confirmation.hide()' type='button' /> </p:confirmDialog> </h:form> </ui:define> </ui:composition> </html>
更新了ProductSearchBean.java
package com.borislam.view; import java.util.List; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Component; import com.borislam.domain.Product; import com.borislam.service.ProductService; @Component @Scope('session') public class ProductSearchBean { private Product selectedProduct; private ProductSearchCriteria criteria = new ProductSearchCriteria(); private List<Product> productList; public Product getSelectedProduct() { return selectedProduct; } public void setSelectedProduct(Product selectedProduct) { this.selectedProduct = selectedProduct; } public List<Product> getProductList() { return productList; } public void setProductList(List<Product> productList) { this.productList = productList; } public ProductSearchCriteria getCriteria() { return criteria; } public void setCriteria(ProductSearchCriteria criteria) { this.criteria = criteria; } @Autowired private ProductService productService; public void doSearch(ActionEvent event){ productList= productService.searchByCriteria(criteria); } public String doEditDetail() { (FacesContext.getCurrentInstance().getExternalContext().getFlash()).put('selected', selectedProduct); return 'detail.xhtml'; } public void doDelete(ActionEvent event){ try { productService.deleteProduct(selectedProduct); FacesContext context = FacesContext.getCurrentInstance(); context.addMessage(null, new FacesMessage('Delete Successfully!')); } catch (DataAccessException e ) { FacesContext context = FacesContext.getCurrentInstance(); context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,'Error when deleting product!',null)); } } }
添加了产品详细信息页面以查看产品详细信息。 产品的创建和版本在产品详细信息页面中完成。
detail.xhtml
<html xmlns='http://www.w3.org/1999/xhtml' xmlns:ui='http://java.sun.com/jsf/facelets' xmlns:h='http://java.sun.com/jsf/html' xmlns:f='http://java.sun.com/jsf/core' xmlns:p='http://primefaces.org/ui'> <ui:composition template='/template/common.xhtml'> <ui:define name='pageTitle'> <h:outputText value='Product Search' /> </ui:define> <ui:define name='content'> <f:event listener='#{productDetailBean.initProduct}' type='preRenderView' /> <h:form id='mainForm'> <p:growl id='mainGrowl' sticky='true' /> <p:panelGrid style='width:1024px'> <f:facet name='header'> <p:row> <p:column colspan='2'> Product Details </p:column> </p:row> </f:facet> <p:row> <p:column> <h:outputLabel for='sku' value='sku: *' /> </p:column> <p:column> <p:inputText id='sku' required='true' value='#{productDetailBean.product.sku}' label='Sku' rendered='#{productDetailBean.newProduct}'/> <h:outputText value='#{productDetailBean.product.sku}' label='Sku' rendered='#{not productDetailBean.newProduct}'/> </p:column> </p:row> <p:row> <p:column> <h:outputLabel for='type' value='Type *' /> </p:column> <p:column> <p:selectOneMenu id='type' required='true' label='Type' valueChangeListener='#{productDetailBean.clearDetails}' value='#{productDetailBean.product.type}' > <f:selectItem itemLabel='Select One' itemValue='' /> <f:selectItem itemLabel='Audio Album' itemValue='Audio Album' /> <f:selectItem itemLabel='Book' itemValue='Book' /> <f:ajax render='buttonPanel trackPanel chapterPanel'/> </p:selectOneMenu> </p:column> </p:row> <p:row> <p:column> <h:outputLabel for='title' value='Title: *' /> </p:column> <p:column> <p:inputText id='title' required='true' value='#{productDetailBean.product.title}' label='Title' /> </p:column> </p:row> <p:row> <p:column> <h:outputLabel for='description' value='Description: *' /> </p:column> <p:column> <p:inputText id='description' required='true' value='#{productDetailBean.product.description}' label='Description' /> </p:column> </p:row> <p:row> <p:column> <h:outputLabel for='publisher' value='Publisher: *' /> </p:column> <p:column> <p:inputText id='publisher' required='true' value='#{productDetailBean.product.publisher}' label='Publisher' /> </p:column> </p:row> <p:row> <p:column> <h:outputLabel for='artist' value='Artist: ' /> </p:column> <p:column> <p:inputText id='artist' value='#{productDetailBean.product.details.artist}' label='Artist' /> </p:column> </p:row> <p:row> <p:column> <h:outputLabel for='listPrice' value='List Price: ' /> </p:column> <p:column> <p:inputText id='listPrice' required='true' value='#{productDetailBean.product.pricing.list}' label='List Price' /> </p:column> </p:row> <p:row> <p:column> <h:outputLabel for='retailPrice' value='Retail Price: ' /> </p:column> <p:column> <p:inputText id='retailPrice' required='true' value='#{productDetailBean.product.pricing.retail}' label='REtail Price' /> </p:column> </p:row> <p:row> <p:column> <h:outputLabel for='author' value='Author: ' /> </p:column> <p:column> <p:inputText id='author' value='#{productDetailBean.product.details.author}' label='Author' /> </p:column> </p:row> <p:row> <p:column> <h:outputLabel for='genre' value='Genre: *' /> </p:column> <p:column> <p:inputText id='genre' required='true' value='#{productDetailBean.product.details.genre}' label='Genre' /> </p:column> </p:row> <p:row> <p:column colspan='2' styleClass='ui-widget-header'> <p:outputPanel id='buttonPanel'> <p:commandButton value='Add Tracks' οnclick='addTrackDlg.show();' type='button' rendered='#{productDetailBean.product.type == 'Audio Album'}'/> <p:commandButton value='Add Chapters' οnclick='addChapterDlg.show();' type='button' rendered='#{productDetailBean.product.type == 'Book'}'/> </p:outputPanel> </p:column> </p:row> <p:row> <p:column colspan='2' > <p:outputPanel id='trackPanel' > <p:dataList value='#{productDetailBean.product.details.tracks}' var='track' type='ordered' rendered='#{productDetailBean.product.details.tracks.size() > 0}'> #{track} </p:dataList> </p:outputPanel> <p:outputPanel id='chapterPanel' > <p:dataList value='#{productDetailBean.product.details.chapters}' var='chapter' type='ordered' rendered='#{productDetailBean.product.details.chapters.size() > 0}'> #{chapter} </p:dataList> </p:outputPanel> </p:column> </p:row> <f:facet name='footer'> <p:row> <p:column colspan='2'> <p:commandButton value='Save' icon='ui-icon-disk' actionListener='#{productDetailBean.doSave}' update='mainGrowl' /> <p:button value='Back to Search' icon='ui-icon-back' outcome='search.xhtml' /> </p:column> </p:row> </f:facet> </p:panelGrid> </h:form> <h:form> <p:growl id='trackGrowl' sticky='true' /> <p:dialog id='addTrackDlg' header='Adding Tracks for the product' widgetVar='addTrackDlg' modal='true' height='100' width='450' resizable='false'> <h:outputLabel for='track' value='Track: ' /> <p:inputText id='track' required='true' value='#{productDetailBean.newTrack}' label='Track' /> <p:commandButton value='Add' actionListener='#{productDetailBean.doAddTracks}' icon='ui-icon-check' update='trackGrowl, :mainForm:trackPanel' oncomplete='addTrackDlg.hide()'/> </p:dialog> </h:form> <h:form> <p:growl id='chapterGrowl' sticky='true' /> <p:dialog id='addChapterDlg' header='Adding Chapters for the product' widgetVar='addChapterDlg' modal='true' height='100' width='450' resizable='false'> <h:outputLabel for='chapter' value='Chapter: ' /> <p:inputText id='chapter' required='true' value='#{productDetailBean.newChapter}' label='Chapter' /> <p:commandButton value='Add' actionListener='#{productDetailBean.doAddChapters}' icon='ui-icon-check' update='chapterGrowl, :mainForm:chapterPanel' oncomplete='addChapterDlg.hide()'/> </p:dialog> </h:form> </ui:define> </ui:composition> </html>
ProductDetailsBean.java
package com.borislam.view; import java.util.ArrayList; import java.util.List; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import javax.faces.event.ValueChangeEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.dao.DataAccessException; import com.borislam.domain.Detail; import com.borislam.domain.Pricing; import com.borislam.domain.Product; import com.borislam.service.ProductService; @Component @Scope('session') public class ProductDetailBean { @Autowired private ProductService productService; private boolean newProduct; private Product product; private String newTrack; private String newChapter; public boolean isNewProduct() { return newProduct; } public void setNewProduct(boolean newProduct) { this.newProduct = newProduct; } public Product getProduct() { return product; } public void setProduct(Product product) { this.product = product; } public String getNewTrack() { return newTrack; } public void setNewTrack(String newTrack) { this.newTrack = newTrack; } public String getNewChapter() { return newChapter; } public void setNewChapter(String newChapter) { this.newChapter = newChapter; } public void initProduct(){ Object selectedProduct = (FacesContext.getCurrentInstance().getExternalContext().getFlash()).get('selected'); if (selectedProduct==null && !FacesContext.getCurrentInstance().isPostback()) { product = new Product(); product.setDetails(new Detail()); product.setPricing(new Pricing(0,0)); setNewProduct(true); } if (selectedProduct!=null) { product = (Product)selectedProduct; setNewProduct(false); } } public void doSave(ActionEvent event) { try { productService.saveProduct(product); FacesContext context = FacesContext.getCurrentInstance(); context.addMessage(null, new FacesMessage('Save Successfully!')); } catch (DataAccessException e) { e.printStackTrace(); FacesContext context = FacesContext.getCurrentInstance(); context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,'Error when saving product!',null)); } } public void doAddTracks(ActionEvent event) { List<String> tracks = product.getDetails().getTracks(); if (CollectionUtils.isEmpty(tracks)) { product.getDetails().setTracks(new ArrayList<String>()); } product.getDetails().getTracks().add(this.newTrack); } public void doAddChapters(ActionEvent event) { List<String> tracks = product.getDetails().getChapters(); if (CollectionUtils.isEmpty(tracks)) { product.getDetails().setChapters(new ArrayList<String>() ); } product.getDetails().getChapters().add(this.newChapter); } public void clearDetails(ValueChangeEvent event) { if ('Audio Album'.equalsIgnoreCase(event.getNewValue().toString()) ) { product.getDetails().setChapters(null); } if ('Book'.equalsIgnoreCase( event.getNewValue().toString())) { product.getDetails().setTracks(null); } } }
更新了ProductService.java
package com.borislam.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.borislam.domain.Product; import com.borislam.repository.ProductRepository; import com.borislam.view.ProductSearchCriteria; @Service public class ProductService { @Autowired private ProductRepository productRepository; public List<Product> searchByCriteria(ProductSearchCriteria criteria){ return productRepository.searchByCriteria(criteria); } public Product getProduct(String sku) { return productRepository.findBySku(sku); } public void saveProduct(Product p){ productRepository.save(p); } public void deleteProduct(Product p){ productRepository.delete(p); } }
结论:
1. Spring Data Mongo DB提供了MongoTemplate,可让您轻松执行MongoDB操作。
2.借助Spring Data MongoDB,可以轻松将MongoDB JSON样式的文档映射到POJO
3. Spring数据的存储库抽象减少了访问MongoDB的样板代码。
4.您将自定义行为添加到spring数据存储库。
参考: 示例应用程序简介(MongoShop产品目录) , MongoDB模式设计和数据准备 , JSF(PrimeFaces)和Spring数据MongoDB集成 , 带有Spring数据存储库和mongotemplate的Enquriy数据 , 创建,编辑和删除数据 (来自我们的JCG合作伙伴 Boris)在“ Programming Peaceally和平”博客上的林先生。
翻译自: https://www.javacodegeeks.com/2013/02/spring-data-mongodb-and-jsf-integration-tutorial.html