Sunday, July 19, 2009

How to use Custom-sql in Liferay?

Step-1:
------

Create the file default-ext.xml under ext-impl/src/custom-sql (cerate custom-sql folder if it is not there)

<?xml version="1.0"?>

<custom-sql>
<sql file="custom-sql/book.xml" />
</custom-sql>

(You can refer default.xml under portal source)

Step-2:
------

Create the file queries.xml, under the same folder, which will contain all the application specific queries as name / value pairs.

<?xml version="1.0"?>
<custom-sql>
<sql id="ur_id">
<![CDATA[
SELECT
{Book.*}
FROM
Book
WHERE
(Book.title like ?)
]]>
</sql>
</custom-sql>


Step-3:
------

Add your entry in portal-ext.properties

custom.sql.configs=\
custom-sql/default.xml, \
custom-sql/default-ext.xml

Step-4:
------

Create the file "BookFinderImpl.java" under service/persistence and do the required imports

public class BookFinderImpl extends BasePersistenceImpl implements
BookFinder{

}


Step-5:
------

Do ant build-service, so that the necessary interface is generated.

Step-6:
------

Now write the actual logic to access the custom SQL. You need to update the BookFinderImpl
we developed in the previous step.

// the name of the query
public static String GET_QUERY = "id_given_in_queries.xml";


// the method which will be called from the ServiceImpl class
public List getBooks(String pattern) throws SystemException {

Session session = null;
try {
// open a new hibernate session in normal case when you are opening session for same entity
session = openSession();
// In case of other entity you set the session first like below then open the session
setSessionFactory((SessionFactory)PortalBeanLocatorUtil.getBeanLocator().locate(TagsAssetModelImpl.SESSION_FACTORY));
session = openSession();

// pull out our query from book.xml, created earlier
String sql = CustomSQLUtil.get(GET_QUERY);

// create a SQLQuery object
SQLQuery q = session.createSQLQuery(sql);

//In normal case : In our case book use this
q.addEntity("Book", BookImpl.class);

//In other case : In our case TagsAsset use like this
q.addEntity(TagsAssetModelImpl.TABLE_NAME, TagsAssetImpl.class);


// Get query position instance
QueryPos qPos = QueryPos.getInstance(q);

// fill in the "?" value of the custom query
// this is same like forming a prepared statement
qPos.add(pattern);

// execute the query and return a list from the db
return (List)q.list();

/*
// use this block if you want to return the no. of rows (count)

int rows = 0;

Iterator itr = q.list().iterator();

if (itr.hasNext()) { Long count = itr.next();

if (count != null) { rows = count.intValue(); } }

return rows;
*/
} catch (Exception e) {
throw new SystemException(e);
} finally {
closeSession(session);
}
}

Make the necessary additional imports.

import java.util.List;

import com.ext.portlet.library.model.Book;
import com.ext.portlet.library.model.impl.BookImpl;
import com.liferay.portal.SystemException;
import com.liferay.portal.kernel.dao.orm.QueryPos;
import com.liferay.portal.kernel.dao.orm.SQLQuery;
import com.liferay.portal.kernel.dao.orm.Session;
import com.liferay.util.dao.orm.CustomSQLUtil;

Note:

To get the result between a start and end index, you have to use,

QueryUtil.list(q, getDialect(), begin, end);

in the place of

q.list();

where, you will pass the parameters (begin and end) from your ServiceImpl class.

Step-7:
------

write the method in BookLocalServiceImpl.java

public List searchBook(String title) throws PortalException,
SystemException, RemoteException {

// return bookPersistence.findByTitle(title);
return BookFinderUtil.getBooks("%" + title + "%");
}


Step-8:
------

run "ant build-service" again passing the service.xml file as parameter.

This will update the corresponding interface with the new method defined.


Step 9:
-------

Now go ahead and call BookLocalServiceImpl method from your jsp or java normally how you call other methods

Thanks

22 comments:

Liferay said...

Its fantastic guide from you. I solved my problem by reading your blogs.

Unknown said...

this does not work for me maybe I am missing something

Unknown said...

hi i am new to liferay . i am trying out hooks concept.can u just say what is hook and use of it?

kamalkant said...

Hi Hemalatha,
Using hooks feature we can achieve customizing level between ext and plugins as plugins don't support whole customization what ext support but some time ext is too heavy too use. For more details u can look into following link .
Http://www.liferay.com/web/raymond.auge/blog/-/blogs/portal-hook-plugins

kamalkant said...

Hi Banji,
Can you please tell me where u got stuck??

Unknown said...

Hi,

I am trying to create custom query. But I see, that service builder does not generate interface. As far as I understand after creating BookFinderImpl class, service builder should generate BookFinder interface? O maybe I am wrong?

Unknown said...

Hi KB,
service builder will generate interface only when you are putting BookFinderImpl under
ext-impl/[$your_portlet_service_path$]/persistence. This class ie BookFinderImpl will extend BasePersistenceImpl and will implement [$Your_Entity$]Finder in this case [$Your_Entity$]= Book.
Thanks
Kamal

Unknown said...

Hello,

thank you for quick reply. I do everything like you wrote here. Ok, I will try to give you more information.
I am using Liferay 5.2.3 version, using Ext development environment. I created Impl class as you said, ran servicebuilder, but no interfaces where generated, no Util class was generated. Ok, so I did everything myself. And then I try to call Util class method *FINDER* but I got exception "NullPointerException". The problem is that *Util class method *finder* tries to call getFinder() and _finder instance is not instantiated. It is not, because service builder does not regenerate ext-spring.xml and there is no definition for that bean.
Then I put definition manually to the ext-spring.xml, everything works fine.

Or maybe this is not the way everything supposed to work?

Unknown said...

Hi KB,
Ideally it should generate all the related files. Any way thanks for sharing your knowledge.

remo said...

Hi, i'm sorry my english but i have a little problem. I have followed the post(very good the guide), this work for me but the query is very very slow(15s), the query is a simple select column from table; and when i use the method predefinided 'findAll()' is fast(2s). What is the problem?

i work with apache tomcat 6.0.20, Liferay 5.2.3, oracle 10g, ext environment and plugin sdk in Debian.

Darshil said...

HI
i am new to liferay.
I read the above way to write custom queries.
Will it work if i do the same in plugins?
I am asking as i see some files to be updated in ext-impl

-Darshil

Shreenivasan said...

Hello,
Can you please tell me, where should I write BookFinderImpl.java calss? Will it be in ext-impl or there will be any portlet for it? Do I need to create any service.xml file?
-Shreenivasan

Unknown said...

2 Shreenivasan: Hello, you do not need to write anything into service.xml. Impl class should be placed in com.ext.portlet.YOUR_PORTLET.service.persistence directory.

Unknown said...

2 Shreenivasan and Darshil : KB is right you don't have r8 any thing service.xml changes will only happen under YOUR_PORTLET.service.persistence directory

dark angel said...

hi, I am using liferay 4.2
and I couldn't resolve these import in BookFinderImpl:
import com.liferay.portal.kernel.dao.orm.QueryPos;
import com.liferay.portal.kernel.dao.orm.SQLQuery;
import com.liferay.portal.kernel.dao.orm.Session;
import com.liferay.util.dao.orm.CustomSQLUtil;

Unknown said...

import com.liferay.util.dao.orm.CustomSQLUtil;
import com.liferay.portal.kernel.dao.orm.QueryPos;
For these check under com.liferay.util.dao.hibernate

t4pham said...

Hi Kamalkant,

I followed this but got an exception when run tomcat server (Liferay 5.2.3 + tomcat 6.0):

ERROR [PortalInstances:300] com.liferay.portal.SystemException: com.liferay.portal.kernel.dao.orm.ORMException
com.liferay.portal.SystemException: com.liferay.portal.kernel.dao.orm.ORMException
at com.liferay.portal.service.persistence.RoleFinderImpl.findBySystem(RoleFinderImpl.java:235)
at com.liferay.portal.service.impl.RoleLocalServiceImpl.checkSystemRoles(RoleLocalServiceImpl.java:130)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

Any thing wrong in my configuration?
Thanks!

t4pham said...

@Hi,

I do ant clean target and restart server then it run fine, thanks

۩ Ravi Kumar Gupta ~A.K.A~ "D'Maverick" ۩ said...

Thanks buddy.. that helped a lot.

Jignesh Vachhani said...

You can have more details about custom query on http://www.liferaysolution.com/2012/03/custome-query.html as well

Gnanasekar said...

hi i am new to life ray can u explain how to connect the liferay to mysql and how to use.....

Unknown said...

@Gnanasekar : which version of liferay you are using ??