注:本篇博客所讲的代码内容基于前面的CRUD-demo的代码。看此篇文章前,请先阅读以下两篇博客
Liferay6.1简单增删改查示例:http://www.huqiwen.com/2012/11/01/liferay6-1-crud-demo/
Liferay 6.1开发学习(四):Service Builder:http://www.huqiwen.com/2012/09/11/liferay-6-1-development-study-4-service-builder/
在Liferay的二次开发中,经常使用到ServiceBuilder,它为我们生成了数据库操作层、业务层、SQL语句等内容,里面包含基础的增删改查的语句,可以满足简单的业务场景,但对于一些复杂的业务场景,可能需要有复杂的查询,单表的多字段联合查询,跨表的查询等等。在这里介绍几种常用的基于ServiceBuilder的自定义查询方法。
在service.xml中定义一个如下的方法:
1. <finder name="userId" return-type="Collection">
2. <finder-column name="userId" />
3. <finder-column name="groupId"/>
4. </finder>
表示的意思为,我们生成一个叫findByUserId的方法,其中有两个参数为userId和groupId,返回的类型为一个集合类型,也就是List<xxx>,其中的xxx为这个service.xml中相应的entity的name值。
注意:finde-column 里面的name值必须和前面设置的column的值一致。
此处同时可以添加比较符号,如=, !=, <, <=, >, >=, 或者LIKE,默认为=,如果要添加其他比较符合,添加一个属性,如<finder-column name="bookName" comparator="LIKE"/>
重新执行serviceBuilder。会为我们生成一个public List<Books> findByUserId(long userId,long groupId)的方法。此方法我们可以通过xxxUtil.findByUserId进行调用,在此示例中就是使用BooksUtil类进行调用。
但是在portlet中我们一般不直接使用xxUtil方法,所以要通过BooksLocalServiceUtil方法进行调用,需要再进行一层包装,找到xxx.service.impl里面的BooksLocalServiceImpl类,我们在里面添加一个方法名为getAllBooks,然后在这个方法里面写booksPersistence.findByUserId(userId,groupId)。写完之后重新执行ServiceBuilder。(具体体代码参看CURD-demo里面的)
Liferay中的DynamicQuery是封装的hibernate的querycriteria,所以查询语法上基本和hibernate的querycriteria查询类似。比如现在我们要在Books这个表里面查询,实现bookName包含某一个关键字,而且groupId等于xxx的情况,代码如下。
1. DynamicQuery query = DynamicQueryFactoryUtil.forClass(Books.class);
2. if(Validator.isNotNull(keyWord)){
3. query.add(PropertyFactoryUtil.forName("bookName").like("%"+keyWord+"%"));
4. }
5. query.add(PropertyFactoryUtil.forName("groupId").eq(groupId));
6.
7. dynamicQuery(query,start,end);
这几行代码的意思,是第一行相当于创建SQL:select * from books。
第2-4行为当keyword不为空的时候,为将上面的sql拼接为 select * from books where bookName like ‘%keyWord%’。
第5行为,再添加 and groupid=groupId
最后一行为执行查询,并返回相应的查询结果。
使用DynamicQuery在进行一些简单的查询时非常方便,如果要进行一些复杂的查询,如跨多表的查询等,使用DynamicQuery就会比较复杂;而且使用DynamicQuery查询返回的数据类型要么是现有的对象,要么是对象集合,有时候我们的查询可能需要返回的数据是某几个对象部分属性的组合等,这个时候可以使用HQL进行查询,在基于ServiceBuilder使用HQL时,有点小复杂,方法如下。
第一步:在xxx.service.persistence里面新建BooksFinderImpl方法,继承自BasePersistenceImpl类。此处的命名必须是xxFinderImpl,前面的xx是Service.xml里面定义的实体名称。
第二步:执行service builder,此时会在service包的xxx.service.persistence下面生成BooksFinder的接口类和对应的BooksFinderUtil类。
第三步:让我们的BooksFinderImpl继承BooksFinder类。现在我们的BooksFinderImpl类如下。
1. public class BooksFinderImpl extends BasePersistenceImpl<Books> implements BooksFinder {
2. }
第四步:在此类中编写我们的具体的查询方法(只是简单的示例代码),如下。
1. public List findBooks(long userId, String keywords, int start, int end) throws SystemException{
2. List list = null;
3. String sql = null;
4. sql = "SELECT book.bookId, book.bookNo,book.bookName FROM Books book where book.userId=? and book.bookName =?";
5.
6. Session session = null;
7. try {
8. session = openSession();
9. Query q = session.createQuery(sql);
10. q.setLong(0, userId);
11. q.setString(1, keywords);
12.
13. list = (List)QueryUtil.list(q, getDialect(),start, end, false);
14. }
15. catch (Exception e) {
16. throw processException(e);
17. }
18. finally {
19. closeSession(session);
20. }
21.
22. return list;
23. }
第五步:重新执行ServiceBuilder,现在会在BooksFinderUtil里面生成相应的接口,但是我们不能直接调用BooksFinderUtil方法,需要将我们的这个方法添加到BooksLocalServiceImpl里面。我们在BooksLocalServiceImpl里面添加相应的方法,在BooksLocalServiceImpl里面使用booksFinder.findBooks()进行调用。
第六步:再次执行ServiceBuilder,现在就可以通过BooksLocalServiceUtil类调用自定义的查询类了。
PS:建一Liferay技术研究群:6537876,欢迎有兴趣的朋友加入。
以前写过一篇:Liferay 6.1开发学习(十八):Liferay开发过程中的一些常见问题,那篇文章主要关注开发过程中的一些问题,此篇文章主要关注管理部分的常见问题,不定期持续更新。
在Liferay的管理中,有时候我们需要手动的去修改数据库里面的信息,但是修改完成之后,发现在Portlet中的数据并没有改变,这是因为数据库缓存的原因,Liferay默认情况下的取数据是从缓存中取的,不是直接从数据库中的,所以如果我们手动的修改了数据库,需要清除一下数据库缓存,方法如下:
进入控制面板-->服务器-->服务器管理-->清除数据库缓存,再次刷新Portlet页面,数据就会和数据库中的保持一致如下图:
注意:虽然我们可以直接手动的修改数据库,但是一般情况下不推荐直接的调整数据库内容,特别是Liferay本身自带的一些表,如用户、组织机构、页面、站点等表,修改可能问题不大,直接删除数据可能会造成数据的不能访问,因为这些核心表,与其他表有不少关联,在删除的时候可能会导航数据不完整,从而导致页面不能访问等异常情况。除非是已经非常清楚这些表与表的关系,数据的关联,否则修改这些核心表请慎重。
这种情况一般出现在我们修改了环境,比如将环境从开发的机器到复制到了生产服务器上面,发现将war包丢到deploy目录下面没反应,tomcat不会自己开始部署,这个原因一般是由于liferay.home位置错的原因。由于环境的改变,导致liferay.home的目录还是指向的开发的目录,这个时候修改portal-setup-wizard.properties里面的Liferay.home目录,让他指向tomcat所在的目录即可。
在Liferay启动的时候报有,如下的错误com.liferay.portal.kernel.messaging.MessageListenerException: com.liferay.portlet.documentlibrary.DuplicateFileException: welcome_bg_3.jpg。
这个错误其实不影响,如果不想折腾,忽略就行,不影响任务功能的使用。
如果想解决,删除tomcat/webapps下面的welcome-theme和resources-importer-web目录,然后重启即可。
那这个问题是啥意思?为什么会有时候出现,有时候不出现呢?
这个错误的意思是告诉我们welcome_bg_3.jpg图片已经存在了,不能再次导入。这个图片存在于resources-importer-web目录里面,在tomcat加载welcome-theme的时候,里面有资源导入程序,会将这个图片导入到liferay的文档媒体库中,第一次运行的时候导入资源,但是有时候当再次运行的时候,有时还会再加载这个资源导入程序,导入的时候Liferay发现这个文件在我的文档媒体库中已经有了,就报一个这样的错误:告诉我们这个文件已经存在,不能再导入了。Liferay默认不能导入重名的文件。
经常见到有人问,Liferay中怎么删除用户?Lifery中的用户不能直接一次性删除,需要先将用户调整为不活跃状态(撤销),然后才能删除。方法步骤如下:
1、在控制面板的用户和组织里面,找到相应的用户,点击操作中的“撤销”(这个翻译的有点蛋疼,准确翻译注销更合适)
2、点击用户那里搜索所有用户,状态选择为不活跃,搜索
3、在这里就可以对相应的用户进行删除操作
有一些业务系统我们需要没有登录的用户只能看到登录页面,其他页面都看不到。在讲实现之前,首先我们要理解一点,在Liferay中准确来的讲登录页面是一个拥有登录portlet的页面,和我们普通的web开发页面有差异,只要在页面上有登录的portlet,所有的页面都可以是登录页面。
在liferay默认情况下,我们可以在http://localhost:8080/c/portal/login进入登录页面,当我们访问一个没有权限的页面的也会跳转到这个登录页面。所以现在归纳一下就是要满足两种情况:
1、当用户进入网站首页的时候看到的是登录页面。
2、当用户访问没有权限的页面的时候进入登录页面。
我们实现上面两点即可,针对1则是在首页上只添加登录的portlet;针对2则是将功能portlet都放到guest(未登录用户)没有查看权限的页面上了。具体步骤如下:
1、liferay的站点有公有页面和私有页面,公有页面默认是guest都有查看权限,私有页面默认guest没有查看权限,所以将所有的功能点都放到私有页面上。
2、公有页面只保留一个页面,默认的guest/home即可(不要将此页面的guest查看权限也去掉,至少要保留一个guest可以访问的公有页面,不然没有登录的用户没有任何页面的权限,怎么进入登录页面?),在此页面上添加“登录”的portlet。
3、默认的没有权限或session超时会跳转到/c/portal/login页面,如果我想跳转到/guest/login页面怎么办?在portal.properties里面设置 auth.login.url=/web/guest/login