在Liferay的开发过程中,有时需要用到联表查询。暂时还没吃透Liferay本身它自己是如何实现这个的。但是在Liferay开发社区上看到关于自定义SQL正确用法,特此记录下。
一、创建自定义SQL文件。这个是有明确要求的,包括文件名称,目录位置,SQL的写法等等。
在你的Portlet项目docroot/WEB-INF/src/目录下创建“custom-sql”的目录,在此目录中创建default.xml文件。文件格式如下:
<custom-sql>
<sql id="建议用对应使用类的全路径包括方法名做为ID值">
SQL query wrapped in <![CDATA[...]]>
No terminating semi-colon
</sql>
</custom-sql>
对应SQL必须写在<![CDATA[xxxxxx]]>中。
以下给出一个Demo写法。
<?xml version="1.0" encoding="UTF-8"?>
<custom-sql>
<sql id="com.nosester.portlet.eventlisting.service.persistence.EventFinder.\
findByEventNameEventDescriptionLocationName">
SELECT Event_Event.*
FROM Event_Event
INNER JOIN
Event_Location ON Event_Event.locationId = Event_Location.locationId
WHERE
(Event_Event.name LIKE ?) AND
(Event_Event.description LIKE ?) AND
(Event_Location.name LIKE ?)
</sql>
</custom-sql>
二、创建对应的方法和类
1、在项目中找到xxx.xx.xx.service.persistence这个包,在此包下创建对应实体的FinderImpl类,类名有要求,必须是实体名+FinderImpl。如NewsFinderImpl.java,然后继承BasePersistenceImpl<News>,内容如下:
package xxx.service.persistence;
import com.liferay.portal.service.persistence.impl.BasePersistenceImpl;
import xxx.model.News;
public class NewsFinderImpl extends BasePersistenceImpl<News> {
}
2、运行Service Builder就会自动创建对应的Finder接口和Util类。这时修改NewsFinderImpl实现NewsFinder接口。
package xxx.service.persistence;
import com.liferay.portal.service.persistence.impl.BasePersistenceImpl;
import xxx.model.News;
public class NewsFinderImpl extends BasePersistenceImpl<News> implements NewsFinder {
}
3、在NewsFinderImpl里增加需要的实现方法。
java.util.List;
com.liferay.portal.kernel.dao.orm.QueryPos;
com.liferay.portal.kernel.dao.orm.QueryUtil;
com.liferay.portal.kernel.dao.orm.SQLQuery;
com.liferay.portal.kernel.dao.orm.Session;
com.liferay.portal.kernel.exception.SystemException;
com.liferay.portal.service.persistence.impl.BasePersistenceImpl;
com.liferay.util.dao.orm.CustomSQLUtil;
com.xxxx.model.News;
com.xxxx.model.impl.NewsImpl;
public List<News> findByNewsxxxxx(
String a, String b, String c,
int begin, int end) {
Session session = null;
try {
session = openSession();
String sql = CustomSQLUtil.get(
FIND_BY_xxxx);
SQLQuery q = session.createSQLQuery(sql);
q.setCacheable(false);
q.addEntity("News", NewsImpl.class);
QueryPos qPos = QueryPos.getInstance(q);
qPos.add(a);
qPos.add(b);
qPos.add(c);
return (List<News>) QueryUtil.list(q, getDialect(), begin, end);
} catch (Exception e) {
try {
throw new SystemException(e);
} catch (SystemException se) {
se.printStackTrace();
}
} finally {
closeSession(session);
}
return null;
}
在这一步需要特别注意q.addEntity()这个方法,设置此方法就可以将查询结果自动封闭成对应的实体对象,否则返回的结果仅是一个List<List>的对应,不好取结果。
还需要留意CustomSQLUtil.get(xxx)这个方法,此工具会自动扫描刚才定义的SQL文件,可以根据ID值取对应的语句。而且做到语句和类分离,修改方便,一改以往把语句写到类里的不良习惯。
4、向NewsLocalServiceImpl类再增加对应的调用方法。在Liferay Portlet开发中对外提供服务的都是xxxLocalServiceUtils这样的类,正确的姿势是:在xxLocalServiceImpl增加方法,在方法里通过xxFinder类调用自己实现的方法。
5、再运行一次Service Builder就会在xxxLocalServiceUtils里增加对应的调用方式供Portlet调用。
对于刚接触Liferay开发的人来说,要经常运行ServiceBuilder会有点不习惯,但是正因为通过这个工具才大大减少了很多工作,而且所有创建的类和方法都很标准。
记录完毕,断续撸码去。