Liferay Service Builder let you easily generate lot of service classes that give you access to your database. It’s pretty useful when you have basic needs and want to generate basic Java service layer (CRUD + finders).

(nowadays using Hibernate (without Service Builder) should be the best option when working with non-Liferay tables)

But it still has lot of drawbacks (in my opinion), one of them is that there are not a lot of types that can be used for column types.

Simple example

Let’s say you get a table named “Car_Price”, pretty dumb table: each car has a price.

Three columns in your table:

  • carPriceId : BIGINT (PK)
  • carId : BIGINT (id of the car)
  • price : DECIMAL(9, 2)

Very stupid table, no currency, no other fields. Now let’s use Liferay Service Builder to generate the service layer for this table.

In a first attempt you might want to try to write this:

<entity name="CarPrice" local-service="true" remote-service="false">
  <column name="carPriceId" type="long" primary="true" />
  <column name="carId" type="long" />
  <column name="price" type="BigDecimal" />
</entity>

Or

<entity name="CarPrice" local-service="true" remote-service="false">
  <column name="carPriceId" type="long" primary="true" />
  <column name="carId" type="long" />
  <column name="price" type="java.math.BigDecimal" />
</entity>

(Don’t use double & float data types for mapping decimal fields from SQL to Java, here is why)

When building Java services you’ll get this error:

Error on line 93, column 33 in com/liferay/portal/tools/servicebuilder/dependencies/model_impl.ftl
serviceBuilder.getSqlType(packagePath + ".model." + entity.getName(), column.getName(), column.getType()) is undefined.
It cannot be assigned to sqlType
The problematic instruction:
----------
==> assignment: sqlType=serviceBuilder.getSqlType(packagePath + ".model." + entity.getName(), column.getName(), column.getType()) [on line 93, column 33 in com/liferay/portal/tools/servicebuilder/dependencies/model_impl.ftl]
----------

Let’s have a look to lhe line 93 of file “model_impl.ftl” :

<#assign sqlType = serviceBuilder.getSqlType(packagePath + ".model." + entity.getName(), column.getName(), column.getType())>

The specified type “BigDecimal” does not exist in list of available types in Liferay Service Builder, thus it’s not usable from service.xml.

Simple solution

Here is a simple solution to “use” our custom type in Service Builder API.

You can create new methods in CarImpl.java that will be available further when manipulating Car entities.

Steps:

  • Declare your field "price" as a "String" in service.xml
  • In CarImpl.java, create a method with following signature:
public BigDecimal getPrice() {
    return NumberUtils.createBigDecimal(super.getPrice());
}
  • In CarImpl.java, override old getPrice method and set it as deprecated:
/**
 * Use the method #getPrice() that returns a BigDecimal instead.
 */
@Override @Deprecated
public String getPrice() {
    return super.getPrice();
}
  • Rerun service builder, your new methods will be available from all Car entities! Setting the original getPrice method as deprecated is just to tell developers not to use this method (it's deprecated but still it has to have the same implementation as before, otherwise you will encounter issues)

Complex way

If you have more time you can still improve the service builder itself: see here

Otherwise you can stop using it for your custom tables and use Hibernate by yourself :)