Monday, April 20, 2009

JavaScript Method Overloading

JavaScript has some OO features, but it does not really support method overloading. If you defined two methods with the same name but different input parameters, it won't throw any syntax error. However, it always calls the second one at runtime. A reference is here.

Sunday, April 19, 2009

Closed Connection with getMetaData in Spring StoredProcedure

When using Spring's StoredProcedure, it runs into the error "Closed Connection" with Connection.getMetaData().

Error while extracting database product name - falling back to empty error codes
org.springframework.jdbc.support.MetaDataAccessException: Error while extracting DatabaseMetaData; nested exception is java.sql.SQLException: Closed Connection
java.sql.SQLException: Closed Connection


This might have something to do with the networks ports. A detailed description is here. This needs to be revisited.

Wednesday, April 15, 2009

Bind a list to HTML select with Spring

Let's say you have a list of objects that you want to bind as HTML SELECT in JSP. You need to populate the list with data in referenceData(). Note it has to be a list of objects with getters and setters for properties (or Spring PropertyValue object). Next you can add the following code in JSP.



"aProperty" here is the name of the property in the command object that you want to bind the selected option to.

Tuesday, April 14, 2009

Debug JavaScript in IE with Microsoft Script Debugger

You can use Microsoft Script Debugger to debug JavaScript in IE. This blog has details about its installation and setting up IE. When a JavaScript error occurs in IE, you can click OK to debug. It will then open Microsoft Script Debugger and highlight where the error is.

Maintain the order of List in Spring Bind

In most cases, one-to-many is mapped as Set in Hibernate. Set is not ordered. If a domain object has Set, Set is converted to List in DTO to bind it with Spring. If that's the case, it is very important to maintain the order. Here is a use case. A Spring form controller is set to sessionForm= false and the DTO is read in formBackingObject().

  1. A user comes to the form the first time. formBackingObject() is called. Domain object is read. DTO is created. Set is converted to List.
  2. Form is rendered. Elemenets in List is created in HTML.
  3. User fills out data and submits.
  4. Since sessionForm is false, formBackingObject() is called again BEFORE onSubmit(). Again, Domain object is read. DTO is created. Set is converted to List. However, please note that when the domain object is read this time, the order of elements in Set could be different from Step 1. Then the order of elements in List is different the ones in Step 1.
  5. Binder.bind() happens. Bind is still binding the List data according to the order in Step 1. But the actual order of List data is as in Step 4. This means that the binding binds the data of 1st element to that of the 3rd element, for example.
  6. Now it comes to onSubmit(). The DTO's list data has the wrong binding. Once this data is processed at the service/DAO, it could run into all kinds of errors/bugs.

To fix this problem, we need to avoid the seond reading in Step 4. This can be done by setting sessionForm=true. Or if it has to be read, we can add "sort" or "order by" in Hibernate mapping for the Set to force it to maintain a consistent order.

Bind indexed properties in Spring

You can use sping:bind to bind indexed properties. This only works with ordered collection such as List, but not Set.



Here are a few things to note.
  1. The syntax of "path=ingredientDtos[${rowId.index}].baselineDate" - this tells Spring to bind these to a list. If you use "c:out" instead of "form:input" or "form:hidden", then the field will not be submitted and bound
  2. Only the fields listed here in this syntax are submitted and bound. For example, if ingredient has a property called "company" and it is not added here with form:input or form:hidden. Then you will find the property is null in onSubmit(). So the object might miss properties unless they are all listed and bound.

Monday, April 13, 2009

Maven help plugin

Not all maven plugins have good documentation. A lot of times I wonder how to run or use a plugin correctly. The maven "help" plugin is a great help here. It shows the goals and usage for a particular plugin. For example, run the command below to find out how to run axis2 aar plugin.

mvn help:describe -DgroupId=org.apache.axis2 -DartifactId=axis2-aar-maven-plugin -Dversion=1.4.1

Friday, April 10, 2009

Log Hibernate SQL Statements and Parameter Values

It is important to see Hibernate SQL Statements and the binding values for parameters in log to troubleshoot and debug. A nice trick here shows how to do it in log4j.

Basically, it is done by adding the following two lines in log4j.properties.
log4j.category.org.hibernate.SQL=DEBUG
log4j.category.org.hibernate.type=TRACE

You also need to change the appender Threshold, for example, CONSOLE.
log4j.appender.CONSOLE.Threshold=TRACE

Thursday, April 9, 2009

Open-Session-In-View



Servlet 2.3 Filter that binds a Hibernate Session to the thread for the entire processing of the request. Intended for the "Open Session in View" pattern, i.e. to allow for lazy loading in web views despite the original transactions already being completed.

NOTE: This filter will by default not flush the Hibernate Session, with the flush mode set to FlushMode.NEVER. It assumes to be used in combination with service layer transactions that care for the flushing: The active transaction manager will temporarily change the flush mode to FlushMode.AUTO during a read-write transaction, with the flush mode reset to FlushMode.NEVER at the end of each transaction. If you intend to use this filter without transactions, consider changing the default flush mode (through the "flushMode" property).

Hibernate schema definition and Oracle public synonym

Make sure that you don't specify schema in Hibernate mappings if you have public synonym defined for tables in Oracle.

Here is the setup. I had a table MY_TABLE mapped in MyTable.hbm.xml. In the database, a public synonym "MyTable" is created for MY_TABLE. I also had two schema definitions in Hiberante. One is the default schema in Spring config.



The second is in MyTable.hbm.xml


At runtime, Hibernate has problem resolving and working on the right table. It worked after schema definitions were removed from both places and left the resolution to Oracle.

Tuesday, April 7, 2009

What is "String..."?

I saw "String..." in a method signature like this. What exactly is "String..."?
public void aMethod(String... args) {
...
}

"..." here is actually sort of a shortcut to Array. So the code above is pretty much the same as
public void aMethod(String[] args) {
...
}

However, when calling aMethod, you can do
aMethod("s1", "s2, "s3");
instead of
aMethod(new String[]{"s1", "s2", "s3"});
That is convenient. This can be used for any types of Array, such as "Integer...".

Monday, April 6, 2009

Loal artifacts on Artifactory

Artifactory is a maven repository server. In addition to link to external repository servers, it can also host artifacts itself. These artifacts can be found at xxx-local when you browse Artifactory, for example, libs-releases-local and libs-snapshots-local. You can export them in admin console.

Thursday, April 2, 2009

Autowire datasource by name for Spring test base class

Suppose you extends AbstractTransactionalJUnit4SpringContextTests but you have multiple datasource beans. When the context is loaded, Spring will complain that it could not setDatasource() for AbstractTransactionalJUnit4SpringContext because there is more than one bean with the type.

The solution is described here in Spring Docs.

If you are extending from a Spring-provided test base class that happens to use @Autowired on one of its setters methods, you might have multiple beans of the affected type defined in your application context: e.g. multiple DataSource beans. In such a case, you may override the setter and use the @Qualifier annotation to indicate a specific target bean as follows:

...
@Override @Autowired
public void setDataSource(@Qualifier("myDataSource") DataSource dataSource) {
super.setDataSource(dataSource);
}
...

Call stored procedures or functions in Spring

A easy way to call stored procedure or function is to use Spring's SimpleJdbcCall class. It wraps up some convenient features. Most likely, you only need to provide in/out parameters. It uses JdbcTemplte internally.

Here is an example to call stored procedure.
SimpleJdbcCall caller = new SimpleJdbcCall(getDataSource())
.withCatalogName("PKG_OPPIN")
.withFunctionName("pc_unsubmit_from_rr");

SqlParameterSource params = new MapSqlParameterSource()
.addValue("p_case_seq", caseSeq)
.addValue("p_task_seq", taskSeq);

// this sp has no return
caller.execute(params);

Here is an example to call function.
SimpleJdbcCall caller = new SimpleJdbcCall(getDataSource())
.withCatalogName("PKG_OPPIN")
.withFunctionName("fn_get_bean_target_date");

MapSqlParameterSource params = new MapSqlParameterSource()
.addValue("p_dpkg_seq", dataPackageNumber);

Date beanTargetDate = caller.executeFunction(Date.class, params);

return beanTargetDate;


To avoid a call to get metadata, you can declare all parameter explicitly. Here is an example.

SimpleJdbcCall caller = new SimpleJdbcCall(getDataSource())
.withCatalogName("PKG_REG_REVIEW")
.withFunctionName("fn_get_task_complete_date")
.withoutProcedureColumnMetaDataAccess()
.declareParameters(
new SqlOutParameter("v_maxdate", Types.DATE),
new SqlParameter("p_case_seq", Types.INTEGER),
new SqlParameter("p_task_seq", Types.INTEGER)
);

MapSqlParameterSource params = new MapSqlParameterSource()
.addValue("p_task_seq", taskSeq)
.addValue("p_case_seq", caseSeq);

Date taskCompletionDate = caller.executeFunction(Date.class, params);

return taskCompletionDate;

The use of withoutProcedureColumnMetaDataAccess() disables metadata reading. Note that the order matters in declareParameters() - it should match exactly the order of the parameters when the procedure/function is called. Another thing to note is that you don't need to use useInParameters() with withoutProcedureColumnMetaDataAccess() because it is only for filtering the metadata.

Another way to call sp/function is to use Spring's StoredProcedure class. But you will need to create a sub-class for it since StoredProcedure is abstract. Here is an example.

MyStoredFunction fn = new MyStoredFunction(getJdbcTemplate(),
"PKG_REG_REVIEW.fn_get_task_complete_date");
fn.declareParameter(new SqlOutParameter("v_maxdate", Types.DATE));
fn.declareParameter(new SqlParameter("p_case_seq", Types.INTEGER));
fn.declareParameter(new SqlParameter("p_task_seq", Types.INTEGER));
fn.compile();

Map params = new HashMap();
params.put("p_case_seq", caseSeq);
params.put("p_task_seq", taskSeq);

Map result = fn.execute(params);
Date taskCompletionDate = (Date) result.get("v_maxdate");
The execute() method always return a Map. You need to get and cast to get the returned data.

Execute stored procedure or function in TOAD

Question:
How do you execute a stored procedure or function and display the result in TOAD?

Answer:
Navigate to the sp or function in schema browser and highlight it. There is a thunder button "Execute Procedure" up there. Click on it to bring up the dialog where you can provide input parameters.

To see output, you need to do things. First, . First, open a output window by View > DBMS Ouput. Second, in the execute dialog, click "Output Options" link and check "Print OUT arguments/RETURN values to DBMS Output". Now the result will be disaplyed in the DBMS Ouput window after you click OK to execute the sp or function.

Wednesday, April 1, 2009

HibernateException: connection proxy not usable after transaction completion

Problem:
Some DAO methods acquire connections by
getSession().connection()
before calling stored procedures. Then they close the connection explicitly with
JdbcUtils.closeConnection()
Then the application throws "HibernateException: connection proxy not usable after transaction completion" from time to time.

Solution:
As described in Hibernate API, "if the session is using aggressive collection release (as in a CMT environment), it is the application's responsibility to close the connection returned by this call. Otherwise, the application should not close the connection". So in this case, it is fixed by remving the code to close connections.