liferay-6.1 - Implement own service - liferay-6

Hey I have create my own service.xml with student. Now o want to add my own searchByName method for student. can you please explain me what to write in StudentLocalServiceImpl.
public class StudentLocalServiceImpl extends StudentLocalServiceBaseImpl {
/*
* NOTE FOR DEVELOPERS:
*
*/
public List<Student> getAll() throws SystemException {
return studentPersistence.findAll();
}
public Student getStudentByName(String name) {
return studentPersistence.
}
// I have created one method getAll. I need help for the another one.
Thanks in Advance.

You would first declare this as a "finder" element in the service.xml within the entity you defined.
e.g.
<finder name="Name" return-type="Student">
<finder-column name="name" />
</finder>
The return-type could also be Collection if wanting a List<Student> as the return type, if name is not unique.
<finder name="Name" return-type="Collection">
<finder-column name="name" />
</finder>
You can also state a comparison operator for the column:
<finder name="NotName" return-type="Collection">
<finder-column name="name" comparator="!=" />
</finder>
A finder can actually declare a unique index as well to be generated on this relation (will be applied to the DB table) by specifying the unique="true" attribute on the finder:
<finder name="Name" return-type="Student" unique="true">
<finder-column name="name" />
</finder>
With this definition and after re-runing ant build-service the studentPersistence will contain new methods using the name of the finder found in the xml element appended with a prefix: countBy, findBy, fetchBy, removeBy, etc.
Finally, your serice method would only need to contain the following (based on the above):
public Student getStudentByName(String name) throws SystemException {
return studentPersistence.findByName(name);
}
HTH

Related

How to set an attribute of type Collection<> to null in a resultMap?

I have a result map that looks like this:
<resultMap id="myMap" type="myEntity">
<id property="id" column="ID" />
<result property="name" column="NAME" />
<collection property="places" ofType="MyPlace" >
<result property="placeName" column="PLACE_NAME" />
</collection>
<resultMap>
<select id="mySelectStatement" parameterType="MyQuery" resultMap="myMap">
....
</select>
In the incoming parameter (MyQuery) of the select statement I have a flag that indicates whether the places should by joined and resolved or left out. Using the <if test="myFlag" />
construct this all works well.
Now, the only problem that I have is the following: When the flag indicates that the places should be resolved but there are no places connected with the entity then the resulting collection is empty (so far so good). However, when the flag indicates that the places should not be resolved, the resulting collection is also empty.
It is no longer decidable whether the field "places" is empty because there are simply no places or because they weren't being resolved at all. What I would like to have is some mechanism that sets the field "places" to ´null´ instead of returning an empty collection in the case that the flag that decides whether the places should be resolved is set to false.
EDIT:
Some more code to better understand the example
// MyEntity.java
public class MyEntity {
private int id;
private String name;
private List<MyPlace> places;
}
// MyQuery.java
public class MyQuery {
private boolean myFlag;
// getter & setter
}
// MyComponent.java
public class MyComponent {
private MyMapper myMapper;
public void findByQuery(MyQuery myQuery) {
List<MyEntity> myEntities = myMapper.mySelectStatement();
MyEntity firstEntity = myEntities.get(0);
List<Place> places = firstEntity.getPlaces();
if(places.isEmpty()) {
System.out.println("Hm I wonder why they are empty");
}
}
}
// MyMapper.java
public interface MyMapper {
List<MyEntity> mySelectStatement(MyQuery myQuery);
}
// MyMapper.xml
// result map from above
<select id="mySelectStatement" parameterType="MyQuery" resultMap="myMap">
SELECT * FROM MY_ENTITY
<if test="myFlag">
LEFT OUTER JOIN PLACES ON .....
</if>
</select>
And some clarification: This all works in principle. The only problem that I have is that I can not distinguish between an empty collection "places" that is empty because there were no entries in the table AND an empty collection places that is empty because they were not supposed to be resolved in the first place.
My current solution is to check in MyComponent after the method call whether the query that was passed in has the flag set to false. If that is the case, the "places" variable is manually set to null.

CFQL not working when a parameter is an enum

I reproduced the problem by creating a small sample with an entity called Employee and an enum called EmployeeStatus. I then created a method called LoadByStatus. The problem is the first line of the method throws an exception when the enum parameter is set to employeed. There are other things wrong as I commented out the if statement and it generated a SQL exception.
public static System.Data.IDataReader PageDataLoadByStatus(CodeFluent.Runtime.PageOptions pageOptions, DemoLoadByEnum.EmployeeStatus status)
{
if ((status == DemoLoadByEnum.EmployeeStatus.Employeed))
{
throw new System.ArgumentNullException("status");
}
CodeFluent.Runtime.CodeFluentPersistence persistence = CodeFluentContext.Get(DemoLoadByEnum.Constants.DemoLoadByEnumStoreName).Persistence;
persistence.CreateStoredProcedureCommand(null, "Employee", "LoadByStatus");
persistence.AddParameterEnumInt32("#status", status, DemoLoadByEnum.EmployeeStatus.Employeed);
if ((pageOptions != null))
{
System.Collections.IEnumerator enumerator = pageOptions.OrderByArguments.GetEnumerator();
bool b;
int index = 0;
for (b = enumerator.MoveNext(); b; b = enumerator.MoveNext())
{
CodeFluent.Runtime.OrderByArgument argument = ((CodeFluent.Runtime.OrderByArgument)(enumerator.Current));
persistence.AddParameter(string.Format("#_orderBy{0}", index), argument.Name);
persistence.AddParameter(string.Format("#_orderByDirection{0}", index), ((int)(argument.Direction)));
index = (index + 1);
}
}
System.Data.IDataReader reader = CodeFluentContext.Get(DemoLoadByEnum.Constants.DemoLoadByEnumStoreName).Persistence.ExecuteReader();
return reader;
}
Below is Employee entity
<cf:entity name="Employee" namespace="DemoLoadByEnum">
<cf:property name="Id" key="true" />
<cf:property name="Name" entityDisplay="true" />
<cf:property name="Status" typeName="{0}.EmployeeStatus" />
<cf:method name="LoadByStatus" body="LOAD(DemoLoadByEnum.EmployeeStatus status) WHERE Status = #status" />
</cf:entity>
Below is my Enum
<cf:enumeration name="EmployeeStatus" namespace="DemoLoadByEnum">
<cf:enumerationValue name="Employeed" />
<cf:enumerationValue name="Released" />
</cf:enumeration>
CodeFluent Entities has a notion of default value. Simon's answer explains this concept: https://stackoverflow.com/a/35790190/2996339
The common way to solve your problem is to add an enumeration value which act as the default value (None/Unset/Unspecified/...), or change the default value.
Another way is to set Use Persistence Default Value to False at method parameter, method or enumeration level.

Indexed getter only in Dozer

I'm struggling with a mapping in Dozer. The basic structure of my classes is this:
class Foo {
private String someString;
public String getSomeString() {
return someString;
}
public void setSomeString(String someString) {
this.someString = someString;
}
}
and the interesting part:
class Bar {
// note that no field is declared
public String[0] getSomeBarString() {
// This returns an array where the acctually desired string is a index 0
}
public void setSomeBarString(String someString) {
// stores the string otherwise
}
}
Compensating the absence of a field and the differently named getter/setter methods was quite easy:
<mapping>
<class-a>Foo</class-a>
<class-b>Bar</class-b>
<field>
<a>someString</a>
<b get-method="getSomeBarString" set-method="setSomeBarString">someBarString</b>
</field>
</mapping>
From my understanding I could even omitt get-method and set-method as there is no field access by default.
My problem is that the getter is indexed and the setter isn't. I've already read about indexed property mapping but it does it both ways. Is there a way to make only one direction indexed? E.g. would get-method="getSomeBarString[0]" work?
After a night of sleep I got an idea myself. I just define two one way mappings and make one of them indexed. It also turns out indexing is defined the same way (after the property name) even if you declare a different get-method or set-method.
<mapping type="one-way">
<class-a>Foo</class-a>
<class-b>Bar</class-b>
<field>
<a>someString</a>
<b set-method="setSomeBarString">someBarString</b>
</field>
</mapping>
<mapping type="one-way">
<class-a>Bar</class-a>
<class-b>Foo</class-b>
<field>
<a get-method="getSomeBarString">someBarString[0]</a>
<b>someString</b>
</field>
</mapping>

Getting Data from Multiple tables in Liferay 6.0.6

i'm trying to get data from multiple tables in liferay 6.0.6 using custom sql, but for now i'm just able to display data from one table.does any one know how to do that.thanks
UPDATE:
i did found this link http://www.liferaysavvy.com/2013/02/getting-data-from-multiple-tables-in.html but for me it's not working because it gives an error BeanLocator is null,and it seems that it's a bug in liferay 6.0.6
The following technique also works with liferay 6.2-ga1.
We will consider we are in the portlet project fooproject.
Let's say you have two tables: article, and author. Here are the entities in your service.xml :
<entity name="Article" local-service="true">
<column name="id_article" type="long" primary="true" />
<column name="id_author" type="long" />
<column name="title" type="String" />
<column name="content" type="String" />
<column name="writing_date" type="Date" />
</entity>
<entity name="Author" local-service="true">
<column name="id_author" type="long" primary="true" />
<column name="full_name" type="String" />
</entity>
At that point run the service builder to generate the persistence and service layers.
You have to use custom SQL queries as described by Liferay's Documentation to fetch info from multiple databases.
Here is the code of your fooproject-portlet/src/main/ressources/default.xml :
<?xml version="1.0"?>
<custom-sql>
<sql file="custom-sql/full_article.xml" />
</custom-sql>
And the custom request in the fooproject-portlet/src/main/ressources/full_article.xml:
<?xml version="1.0"?>
<custom-sql>
<sql
id="com.myCompany.fooproject.service.persistence.ArticleFinder.findByAuthor">
<![CDATA[
SELECT
Author.full_name AS author_name
Article.title AS article_title,
Article.content AS article_content
Article.writing_date AS writing_date
FROM
fooproject_Article AS Article
INNER JOIN
fooproject_Author AS Author
ON Article.id_author=Author.id_author
WHERE
author_name LIKE ?
]]>
</sql>
</custom-sql>
As you can see, we want to fetch author's name, article's title, article's content and article's date.
So let's allow the service builder to generate a bean that can store all these informations. How ? By adding it to the service.xml ! Be careful: the fields of the bean and the fields' name returned by the query must match.
<entity name="ArticleBean">
<column name="author_name" type="String" primary="true" />
<column name="article_title" type="String" primary="true" />
<column name="article_content" type="String" />
<column name="article_date" type="Date" />
</entity>
Note: defining which field is primary here does not really matter as there will never be anything in the ArticleBean table. It is all about not having exceptions thrown by the service builder while generating the Bean.
The finder method must be implemented then. To do so, create the class com.myCompany.fooproject.service.persistence.impl.ArticleFinderImpl. Populate it with the following content:
public class ArticleFinderImpl extends BasePersistenceImpl<Article> {
}
Use the correct import statements and run the service builder. Let's make that class implement the interface generated by the service builder:
public class ArticleFinderImpl extends BasePersistenceImpl<Article> implements ArticleFinder {
}
And populate it with the actual finder implementation:
public class ArticleFinderImpl extends BasePersistenceImpl<Article> implements ArticleFinder {
// Query id according to liferay's query naming convention
public static final String FIND_BY_AUTHOR = ArticleFinder.class.getName() + ".findByAuthor";
public List<Article> findByAuthor(String author) {
Session session = null;
try {
session = openSession();
// Retrieve query
String sql = CustomSQLUtil.get(FIND_BY_AUTHOR);
SQLQuery q = session.createSQLQuery(sql);
q.setCacheable(false);
// Set the expected output type
q.addEntity("StaffBean", StaffBeanImpl.class);
// Binding arguments to query
QueryPos qpos = QueryPos.getInstance(q);
qpos.add(author);
// Fetching all elements and returning them as a list
return (List<StaffBean>) QueryUtil.list(q, getDialect(), QueryUtil.ALL_POS, QueryUtil.ALL_POS);
} catch(Exception e) {
e.printStackTrace();
} finally {
closeSession(session);
}
return null;
}
}
You can then call this method from your ArticleServiceImpl, whether it is to make a local or a remote API.
Note: it is hack. This is not a perfectly clean way to retrieve data, but it is the "less bad" you can do if you want to use Liferay's Service Builder.

Updating Bean from JSF dataTable

I've got this h:dataTable and form:
<h:form>
<h:dataTable value='#{bean.allData.dataItems}' var='item'>
<h:column>
<h:outputText value='#{item.id}' />
</h:column>
<h:column>
<h:inputText value='#{item.name}' />
</h:column>
</h:dataTable>
<h:commandButton value="Save" action="#{bean.saveEntries}"/>
</h:form>
So the table shows the id and a textbox with the name in. That works. When a user clicks the button, I'd like it to save any changes to the name field.
DataBean has:
private Vector<ItemBean> dataItems;
// constructor here
public void setDataItems(Vector v) {
dataItems = v;
}
public Vector getDataItems() {
return dataItems;
}
and ItemBean has...
private String id;
private String name;
// setter for both
// getter for both
Bean has a variable 'allData' of type 'DataBean'.
Bean also has a method saveEntries() which is currently blank (as I'm not sure how it works). How do I reference the inputs to set these values?
JSF has already done it during UPDATE MODEL VALUES phase. Print/debug the local variables and you'll see. All you need to do in the save method is just persisting the data in some datastore.
Also see the following articles for more background information and examples:
Using datatables.
Debug JSF lifecycle.