Create customised auto-complete

$(document).ready(function() {

$(“#apex-owner-autocomplete”).keyup(function() {

var term = $(‘#apex-owner-autocomplete’).val();

if (term.length > 2) {

$.ajax({

//the servlet address to get json data

url: “<c:url value=”/ajax/apex-parent-name.htm”/>”,

dataType: “json”,

data: {

//parameters that need to forward with the url

name_startsWith: term,

filterName:”PARENT_NAME”

},

success: function(data) {

//remove the existing dropdown list

$(“#apex-owner-list ul li”).remove();

//map function translate all items in an array or array-like object to another array of items.

$.map(data.type, function(item) {

//add each matches to the dropdown list

var matchItem = “<input type=’checkbox’ class=’ownerManager’ name=’matchItem’ value=’” + item.name + “‘>” + item.name;

$(“<li/>”).append(matchItem).prependTo(“#apex-owner-list ul”);

})

if (data.type != null) {

//if there is a match

$(‘#btnOwnerSelect’).show();

$(“#log”).show();

$(“#apex-owner-list ul”).show();

}

},

error:function(XMLHttpRequest, textStatus, errorThrown) {

alert(textStatus);

}

});

}

else {

//remove the dropdown list if the search term has less than 3 character

$(‘#apex-owner-list ul li’).remove();

}

});

$(“#btnOwnerSelect”).click(function() {

//insert selected item from autocomplet dropdown list to the log frame

$(“#apex-owner-list input:checked”).each(function() {

var selectedItem = this.value;

var isSelected = false;

$(“#log div”).each(function() {

//if selected already

if($(this).text().indexOf(selectedItem)>-1){

isSelected = true;

}

});

if(!isSelected){

$(“<div/>”).append(selectedItem + “<span class =’filterRemover’ id=’” + selectedItem+ “‘>remove</span>”).appendTo(“#log”);

}

});

$(“#log div span”).each(function() {

//create hidden field for each selected item in the form

var inputOwner = “<input type=’hidden’ name=’parameters[\"OWNER_MANAGER\"]‘ value=’”+ $(this).attr(“id”) +”‘/>”;

$(inputOwner).appendTo(“#results-filter-form”);

});

//$(“#ownerManagerList”).val(selectedItems);

});

//the remove link is generated dynamically, so live() method should be used here.

$(‘.filterRemover’).live(“click”, function() {

//remove the div that hold the item that has been clicked to remove

$(this).parent().remove();

});

})

Posted in Javascript | Leave a comment

More about Criteria

 

In the
earlier note, I have compared the using named query and criteria in general. In
this note, I will talk a bit more about the criteria, as I have realised one
major advantage of criteria that was not mentioned in the earlier note.

 

Here is the scenario.
You need to load a list of publications belong to "Newspaper","Magazine"
and "Newsletter" section and they all should be contained under the “Hongliang.com”
domain. You should also make the metadata information of the publications
available when they are needed.          

 

Would you be
able to find a way to named query to make the metadata information available
while all class are declared as lazy initialised? Right, this is key point for
this note. With the criteria, you can do that easily.

 

public List<Publication> getPublications() {
    //Step 1        
    Criteria pubCriteria = this.getSession().createCriteria(Publication.class)
            .setFetchMode("pubMetadatas", FetchMode.JOIN).setFetchMode("metadata", FetchMode.JOIN)
            .add(Restrictions.in("media", new String[]{"Newspaper","Magazine", "Newsletter" }))
            .addOrder(Order.asc("title"));
 
    //Setp 2
    Criteria domainCriteria = pubCriteria.createCriteria("pubDomains").createCriteria("domain")
            .add(Restrictions.eq("domainName", "hongliang.com"));
 
    //Setp 3
    return domainCriteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
}

 

(I’d
like to create a DB map for this, but unfortunately the MS Viso crash all the
time) The DAO method can be divided into three steps. The first step creates
the criteria with Publication as root entity. Since we want to have the
metadata information to be accessible via hibernate session, we need to apply some
fetching strategy. In the preceding code, FetchMode.Join has been applied,
which means
Hibernate retrieves
the associated instance or collection in the same
SELECT, using an OUTER JOIN.

You
then need to add the restriction that applied to the criteria, such as “media”
property need to contain values of "Newspaper","Magazine"
and "Newsletter". This has to be added at this step, as pubCriteria
is created with cursor (please correct me, if wrong term used here) pointing to
Publication entity. It is the same reason
Order.asc("title")) need to be
applied here as well.  

 

In
step two, domainCriteria is created as sub criteria of
pubCriteria and its cursor pointing to domain entity. With the same
reason mentioned earlier, the
add(Restrictions.eq("domainName",
"
hongliang.com")) method should be applied here. (If you apply this to
pubCriteria, you will get an exception says something like it can’t find the
property ‘daomainName’ in entity Hongliang.com.Publication)

 

In
step three, though the cursor of domainCriteria is pointing to domain entity,
it still holds the information of the root criteria (
pubCriteria) which includes the join fetch that attached to it. setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) is used to
get distinct instance of the root entity (Publication)

 

NB. If join fetching
(FetchMode.Join) is not appropriate, you may consider using select fetching.
The definition is here.

Select fetching: a second SELECT is
used to retrieve the associated entity or collection. Unless you explicitly
disable lazy fetching by specifying lazy="false", this
second select will only be executed when you access the association.

 

Reference:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html#performance-fetching

 

Posted in Hibernate | Leave a comment

Create PDF file with custom open actions

This
article will illustrate how to dynamically generate a PDF file, which will have
the print dialog box automatically popped up when the file is opened in your
web application.

public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {

    //if you have a password
    String pass = getPassword();
    byte[] password = pass.getBytes();
    Document document = new Document();

    String fileLocation = getPath();
    UrlResource pdf = new UrlResource(fileLocation);
    InputStream input = pdf.getInputStream();

    //if you get the file from local
    //InputStream input = new FileInputStream(getPath.());

    //Reads and parses a PDF document.
    PdfReader pdfReader = new PdfReader(input, password);    

    //if you want to generate the PDF file to the local rather than through the browser
    //OutputStream output = new FileOutputStream("c:\\merge.pdf");
    //PdfWriter writer = PdfWriter.getInstance(document, output);

    //using PdfCopy instead of PdfWriter as everything will be copied to an empty document and PdfCopy has addPage() method to copy each page into this new empty document.
    PdfCopy writer = new PdfCopy(document, response.getOutputStream());

 

    //set up the open actions you want to provide
    writer.setOpenAction(new PdfAction(PdfAction.PRINTDIALOG));
    document.open();

    //Use this method to get the direct content for this document. It is an alternative comparing to use  writer.addPage(page).
    //PdfContentByte cb = writer.getDirectContent();

    PdfImportedPage page;

    for (int i = 1; i < pdfReader.getNumberOfPages() + 1; i++) {
        //Use this method to get a page from other PDF document.
        page = writer.getImportedPage(pdfReader, i);
        writer.addPage(page);
        //cb.addTemplate(page, 0, 0);
    }

    response.setContentType("application/pdf");
    response.setHeader("Cache-Control", "no-cache");
    response.setHeader("Pragma", "no-cache");
    response.setDateHeader("Expires", 0);
    response.getOutputStream().flush();
    document.close();
    return null;

}

The code also provides the
solution to generate the PDF file to the local rather via browser, which has
been commented.

Reference:

http://viralpatel.net/blogs/2009/06/itext-tutorial-merge-split-pdf-files-using-itext-jar.html

http://1t3xt.info/examples/browse/?page=example&id=289

 

 

 

Posted in Dynamic File Generation | Leave a comment

Anotation Basic Use 3

Annotation-based controller configuration

Spring
2.5 introduces further stereotype annotations: @Component, @Service and @Controller.
@Component serves as a generic stereotype for any Spring-managed component;
whereas, @Repository, @Service, and @Controller serve as specializations of @Component
for more specific use cases (e.g., in the persistence, service, and
presentation layers, respectively.

 

Spring
provides the capability of automatically detecting ‘stereotyped’ classes and
registering corresponding BeanDefinitions with the ApplicationContext.

 

To
autodetect these classes and register the corresponding beans requires the
inclusion of the following element in XML where ‘basePackage’ would be a common
parent package for the two classes (or alternatively a comma-separated list
could be specified that included the parent package of each class).

 

<?xml
version="1.0" encoding="UTF-8"?>

<beans
xmlns="http://www.springframework.org/schema/beans&quot;

      
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quot;

      
xmlns:context="http://www.springframework.org/schema/context&quot;

      
xmlns:util="http://www.springframework.org/schema/util&quot;

       xsi:schemaLocation="

                                http://www.springframework.org/schema/beans                                                                                         

                                http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

                                http://www.springframework.org/schema/context

                                http://www.springframework.org/schema/context/spring-context-3.0.xsd

                                http://www.springframework.org/schema/util
                                                                                                             

                                http://www.springframework.org/schema/util/spring-util-3.0.xsd"&gt;

 

    <context:component-scan
base-package="com.hongliang"/>

    ……

</beans>              

 

 

When
a component is autodetected as part of the scanning process, its bean name will
be generated by the BeanNameGenerator strategy known to that scanner. By
default, any Spring ‘stereotype’ annotation (@Component, @Repository, @Service,
and @Controller) that contains a name value will thereby provide that name to
the corresponding bean definition. If such an annotation contains no name value
or for any other detected component (such as those discovered due to custom
filters), the default bean name generator will return the uncapitalized
non-qualified class name.

 

    <bean
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

        <property
name="mappings">

            <props>

                <prop
key="/dataPage.htm">dataPageController</prop>

            </props>

        </property>

</bean>

 

Hence, for the corresponding controller should be defined
like this

@Controller

public class DataPageController {

           

}

 

The
following code is an example of how to inject the DAO.

    private SidDao sidDao;

 

    @Autowired

    public void setSidDao(SidDao sidDao) {

        this.sidDao = sidDao;

}

 

Let’s
start with a form submission and compare it with the SimpleFormController.

 

   
@RequestMapping(method = RequestMethod.GET, params = {"type=bookPage",
"!isSubmit"})                       

       
public String setupForm BookPage ((ModelMap model,                                                                                

                    @RequestParam("bookingType")String bookingType)
{

       
//similar to formBackingObject() method in SimpleFormController

       
BookingForm bookingForm = new BookingForm ();

       
bookingForm.setBookingType();

       
model.put("bookingForm ", bookingForm);

 

       
//Similar to referenceData method to provide the data that will
preloaded in the form             

       
List<Date> availableDatesList = sidDao.get AvailableDates(bookingType);

       
model.put("availableDatesList ", availableDatesList);

       
//Redirect back. Just for demonstration purpose, not recommend

       
return "dataPages/ bookPage ";

   
}

 

The
@RequestMapping annotation is used to map URLs onto an entire class or a
particular handler method. Typically the type-level annotation maps a specific
request path (or path pattern) onto a form controller, with additional
method-level annotations ‘narrowing’ the primary mapping for a specific HTTP
method request method ("GET"/"POST") or specific HTTP
request parameters. The example
@RequestMapping(method = RequestMethod.GET, params =
{"type=bookPage", "!isSubmit"})  
which indicates that
corresponding url should have two parameters “type” and “isSubmit” and value
for type is “bookPage” and value for isSubmit should not be true
.

 

You
can customise the signature for the handler method whatever you like, but in
convention, setupForm() to initialise the form and  processSubmit() for form submission. The
parameters that you can provide includes ModelMap , @RequestParam, HttpServletRequest
and etc (check the doc for annotation org.springframework.web.bind.annotation).

 

In
the procssSubmit() method you might want to include a @ModelAttribute parameter,
which binds a method parameter or method return value to a named model
attribute, exposed to a web view.

 

@RequestMapping(method
= RequestMethod.POST,
params = {"type=

bookPage
", "isSubmit=true"})

public
ModelAndView processSubmit
BookPage (

                @ModelAttribute("bookingForm") BookingForm
bookingForm) throws Exception {

    ModelAndView modelAndView = new ModelAndView("bookSuccessPage ");

    String message = sidDao.bookTicket(bookingForm);

    modelAndView.addObject("message ",
message);

    return modelAndView;

}

 

In
the corresponding jsp page you should have something like this

<form:form
commandName="
bookingForm

" action=”/dataPage.htm" method="POST">

    …………..

<input type="submit"
value="Submit" >

 <input type="hidden" value="
bookPage
" name="type">

    <input type="hidden"
value="true" name="isSubmit">

</form:form>

The
value for commandName should be identical to the argument used in @ModelAttribute
earlier. It is recommended to specify the method for form submission, which
should also be identical to the one declared in the @RequestMapping. This would
be very useful to identify the problem, if your successful page includes a page
that would trigger another controller which applying different request method.
Also be aware that you need to submit the associated parameters in your form.  

Posted in Spring | 2 Comments

A Completed Example for Hibernate

A
Completed Example for Hibernate

In the
following example, I am trying to create a new trial user and create a default
subscription for it based on Hibernate lazy initialisation. In the first part, I
would like to HibernateTransactionManager to look after the transaction, so one
of the Hibernate actions fails will trigger the rollback for the entire
transaction.

    @Transactional

    public UserMain
createTrialUser(TrialForm form) throws Exception {

        //create new trial
user account

        UserMain userMain
= new UserMain();

       
userMain.setFirstname(form.getFirstName());

       
userMain.setLastname(form.getLastName());

       
userMain.setUpdatedAt(new Date());

        if
(userMain.getCreatedAt() == null) {

           
userMain.setCreatedAt(new Date());

        }

        //save the new
user and make it persistent

       
getSession().saveOrUpdate(userMain);

 

        //save the email
information for the user    

        String
emailaddress = form.getEmail();

        if
(StringUtils.isNotEmpty(emailaddress)) {

           
createAssociatedContact(userMain, emailaddress);

        }

        //update the
associate

       
getSession().saveOrUpdate(userMain);

        return userMain;

}

Code 1

I
am using the annotation declaration for using the HibernateTransactionManager
in the preceding code. In order to make it work, I also need to declare it in
the context file.

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quot;

            xmlns:tx=http://www.springframework.org/schema/tx

           

…>

<!– use declarative transactions
@Transactional etc –>

<tx:annotation-driven
transaction-manager="transactionManager" />  

 

 <!– Transaction manager for a single
Hibernate SessionFactory (alternative to JTA) –>

 <bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">

      
<property name="sessionFactory"
ref="sessionFactory"/>

 </bean>

Code 2

In
Code 1, You can see that getSession().saveOrUpdate(userMain) method has been
called twice. The first time, it was called to make the userMain object
persistent, which means the object will have a projection in the database and
an id will be assigned to the object. You have to do this at least before calling
to saveUserTypeDomain(userMain).  The
reason is before some query executions, the Session will execute the SQL
statements needed to synchronize the JDBC connection’s state with the state of
objects held in memory.  This process
called flush. As you can see in code 3, the method is calling a SQL query via
criteria. Any transient object should be saved before the query is called.
Otherwise you would get an “org.hibernate.TransientObjectException:
object references an unsaved transient instance”.

private void saveUserTypeDomain(UserMain userMain) {

    UserType userType =
getUserType("Single");

    UserTypeDomain
userTypeDomain = new UserTypeDomain();

   
userTypeDomain.setDomain("Test");

   
userTypeDomain.setUserMain(userMain);

   
userTypeDomain.setUserType(userType);

    getSession().save(userTypeDomain);

}           

 

public UserType getUserType(String userType){

return (UserType)
getSession().createCriteria(UserType.class)

.add(Restrictions.eq("userTypeDesc",
userType)).uniqueResult();

}

Code 3

What
transient object that should be saved? Let’s look at the method
createAssociatedContact(), which is called just before saveUserTypeDomain().
You probably could identify that method userContact.setUserMain(userMain) is
actually take userMain as an transient object (not attached with session yet).  So, that is clear. The userMain object is the
one you need to save and that has to be done before the call to
saveUserTypeDomain()

private void createAssociatedContact(UserMain userMain, String
emailaddress, UserMain creatorUserMain) {

            //create a new contact    

        Contact contact =
new Contact();

       
contact.setData(emailaddress);

       
contact.setCreatedAt(new Date());

        //save the
contact. Be aware of lazy initilisation

       
getSession().save(contact);

 

            //create a new
UserContact

        UserContact
userContact = new UserContact();

//make sure you userMain object is
persistent and has an identifier

       
userContact.setUserMain(userMain);

       
userContact.setContact(contact);

        userContact.setCreatedAt(new
Date());

       
userContact.setUpdatedAt(new Date());

        //save it

       
getSession().save(userContact);

 

            //assign the
value of UserContact to the user

       
Set<UserContact> contactSet = new HashSet<UserContact>();

        contactSet.add(userContact);

       
userMain.setUserContacts(contactSet);

}

   

Code 4

Make sure you
have saved the object individually as they are all lazy initialized and you
could not modify them in cascaded style until they are attached (via save or
update) to the session. You also need to setUserContacts(), if you need to use
this userMain object/instance to get the UserContacts property in the future,
but it wouldn’t cause problem in the database side, as the tables for
UserContacts  and UserMain (the second
call of getSession().saveOrUpdate(userMain)) will be saved or updated
separately.  

Now we come
to the second part, create a default subscription for the new trial user. I
guess there are not much hassles about how to run procedure.

public void subscribeUserToOffer(UserMain userMain) {

 

        List<Long>
selectedOffers = new ArrayList<Long>();

        SubscriptionOffer
offerIdGold = getSubscriptionOffer("Gold_Memember");

       
if(offerIdGold!=null){

           
selectedOffers.add(offerIdGold.getSubscriptionOfferId());

        }

 

        Connection
connection = null;

        try {

            connection =
getHibernateTemplate().getSessionFactory()

.openStatelessSession().connection();

            for (Long
selectedOfferId : selectedOffers) {

               
CallableStatement callableStatement

                        =
connection.prepareCall("{ call
proc_create_user_subscription(?,?,?,?)}");

               
callableStatement.setObject("in_user_main_username",
userMain.getUsername());

               
callableStatement.setObject("in_subscription_offer_id",
selectedOfferId);

               
callableStatement.setObject("in_subscription_type",
"Trial");

               
callableStatement.setObject("in_subscription_end_accessdate",
getEndAccessDateFromTheDurationEntered(new Long("14")));

               
callableStatement.registerOutParameter("result_code",
java.sql.Types.INTEGER);

                ResultSet
resultSet = callableStatement.executeQuery();

                Long resultCode = new
Long(callableStatement.getLong("result_code"));

               
resultSet.close();

                //whether
should run the following code also depends on

               //the resource configuration in the server,
ie defaultAutoCommit="true"

                //connection.commit();

            }

        } catch
(SQLException e) {

            //copied from
usertools. not sure why doing this.

            //it will be
caught later anyway as a failure of creating a new trial user

           
e.printStackTrace();

            org.hibernate.HibernateException hE =
new HibernateException(e);

            throw
SessionFactoryUtils.convertHibernateAccessException(hE);

        } finally {

            if (connection
!= null) {

                try {

                   
connection.commit();

                   
connection.close();

                } catch
(SQLException ignore) {

                }

            }

        }

}

Code 5

What
I would like to stress here is where should this method be called. Inside createTrialUser()?
You might think if you called this subscribeUserToOffer()  method inside createTrialUser(), if the
procedure fail, the transaction manager can just roll it back. Well, I actually
did think like that. However, this could be a mission impossible. The procedure
is totally separate thing from the transaction. In the situation when the
procedure is called, and it needs to call some of the table that has been
update by the earlier Hibernate action (such as, adding new user contact), it would
be no wonder if it just throws you an exception. The procedure would not be
able to call the synchronised tables until the transaction is committed, while
the procedure itself is called within the transaction.
There
is an alternation if you put getSession().flush() before you call the procedure
within the transaction. (No recommended, as it is more like a separate
function).
 Flush() will synchronize
the JDBC connection’s state with the state of objects held in memory.

One
last thing to mention is whether
connection.commit() should be
called in the procedure. If the resource value in the context file of your application
server has configured to
defaultAutoCommit="true", you should
never call the manually call the commit again, as commented in the code 5.

 

Reference:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/objectstate.html#objectstate-flushing

 

Posted in Hibernate | 1 Comment

Using XML XSL transformation in depth

Using XML XSL transformation in depth

It is very convenient to transform an XML source file to a HTML page using XSL style sheet via JSTL x: tag.

<x:transform doc="${XML}" xslt="${XSL}">

 

However, in some occasion, you might want to pass some dynamic parameters into the HTML page you are about to generate, for example, the current date.

<x:transform doc="${XML}" xslt="${XSL}">

                <x:param name="currentDate" value="${currentDate}"/>   

</x:transform>

 

The preceding code will help you to inject the parameters into the transformation. You can define how to use this parameter in your XSL file

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

    <xsl:output method="html" omit-xml-declaration="yes" encoding="ISO-8859-1" indent="yes"/>

<xsl:param name="currentDate"/>

<xsl:template match="/">

         <xsl:value-of select="$currentDate"/>

</xsl:stylesheet>

 

How about when you want to use the parameter in a hyperlink?

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

    <xsl:output method="html" omit-xml-declaration="yes" encoding="ISO-8859-1" indent="yes"/>

<xsl:param name="currentDate"/>

<xsl:template match="/">

                    <xsl:attribute name="href">http://hongliang.com/

                        <xsl:value-of select="$currentDate"/>.html              

                    </xsl:attribute>

</xsl:stylesheet>

 

Let’s make it more complex. How about you need to get the XML source from a remote GZIP file? We would need to use Apache HttpClient for downloading and java.util.zip for convert GZIP file into an input stream of the XML source.

        try {

            // read file from filesystem using http client, then unzip the gzip to inputstream

            HttpMethod getMethod = new GetMethod("http://hongliang.com&quot;);

            getMethod.setQueryString("?a=dongo&filePath=" + filePath);

 

            int status = new HttpClient().executeMethod(getMethod);

            if (status == HttpStatus.SC_OK) {

                InputStream inputStream = getMethod.getResponseBodyAsStream();

                return new ByteArrayInputStream(FileCopyUtils.copyToByteArray(new GZIPInputStream(inputStream)));

            }

        } catch (IOException e) {

            log.error("Failed to ungzip file to server", e);

        }

 

FileCopyUtils.copyToByteArray() from org.springframework.util is used create a copy of the XML source input stream into a new byte array. Since we are using this XML byte input stream as transformation source, we need to convert this it into StreamSource (which implements Source). If your application use Spring MVC, you probably can use the following.

 

ModelAndView mav = new ModelAndView(getViewName());

mav.addObject("XML", new StreamSource(xml));

 

 

Posted in XML | 2 Comments

Spring Data Access Support

Consistent abstract classes
for DAO support

To
make it easier to work with a variety of data access technologies such as JDBC,
JDO and Hibernate in a consistent way, Spring provides a set of abstract DAO
classes that one can extend. These abstract classes have methods for providing
the data source and any other configuration settings that are specific to the
relevant data-access technology.

  • JdbcDaoSupport -
    superclass for JDBC data access objects. Requires a DataSource to be
    provided; in turn, this class provides a JdbcTemplate instance initialized
    from the supplied DataSource to subclasses.
  • HibernateDaoSupport -
    superclass for Hibernate data access objects. Requires a SessionFactory to
    be provided; in turn, this class provides a HibernateTemplate instance
    initialized from the supplied SessionFactory to subclasses. Can
    alternatively be initialized directly via a HibernateTemplate, to reuse
    the latters settings like SessionFactory, flush mode, exception
    translator, and so forth.
  • JdoDaoSupport – super
    class for JDO data access objects. Requires a PersistenceManagerFactory to
    be provided; in turn, this class provides a JdoTemplate instance
    initialized from the supplied PersistenceManagerFactory to subclasses.
  • JpaDaoSupport – super
    class for JPA data access objects. Requires a EntityManagerFactory to be
    provided; in turn, this class provides a JpaTemplate instance initialized
    from the supplied EntityManagerFactory to subclasses.

 

  1. Data access using JDBC

There are a number of options for selecting an
approach to form the basis for your JDBC database access. There are three
flavors of the JdbcTemplate, a new "SimpleJdbc" approach taking
advantage of database metadata, and there is also the "RDBMS Object"
style for a more object oriented approach similar in style to the JDO Query
design. We’ll briefly list the primary reasons why you would pick one of these
approaches. Keep in mind that even if you start using one of these approaches,
you can still mix and match if there is a feature in a different approach that
you would like to take advantage of. All approaches requires a JDBC 2.0
compliant driver and some advanced features require a JDBC 3.0 driver.

  • JdbcTemplate – this is the classic Spring JDBC approach and
    the most widely used. This is the "lowest level" approach and
    all other approaches use a JdbcTemplate under the covers. Works well in a
    JDK 1.4 and higher environment.
  • NamedParameterJdbcTemplate – wraps a JdbcTemplate to provide
    more convenient usage with named parameters instead of the traditional
    JDBC "?" place holders. This provides better documentation and
    ease of use when you have multiple parameters for an SQL statement. Works
    with JDK 1.4 and up.
  • SimpleJdbcTemplate – this class combines the most frequently
    used features of both JdbcTemplate and NamedParameterJdbcTemplate plus it
    adds additional convenience by taking advantage of some Java 5 features
    like varargs, autoboxing and generics to provide an easier to use API.
    Requires JDK 5 or higher.
  • SimpleJdbcInsert and SimpleJdbcCall
    – designed to take advantage of database metadata to limit the amount of
    configuration needed. This will simplify the coding to a point where you
    only need to provide the name of the table or procedure and provide a Map
    of parameters matching the column names. Designed to work together with
    the SimpleJdbcTemplate. Requires JDK 5 or higher and a database that
    provides adequate metadata.
  • RDBMS Objects including MappingSqlQuery, SqlUpdate and StoredProcedure – an approach where you create reusable and
    thread safe objects during initialization of your data access layer. This
    approach is modeled after JDO Query where you define your query string,
    declare parameters and compile the query. Once that is done any execute
    methods can be called multiple times with various parameter values passed
    in. Works with JDK 1.4 and higher.

1.1 JdbcTemplate

public class CalendarDaoImpl extends JdbcDaoSupport implements
CalendarDao {

    public
List<Calendar> getLatestThirtyDays(Long userID, Date startDate, Date
endDate, int typeOne, int typeTwo) throws SQLException {

        return
super.getJdbcTemplate().query("call
proc_return_user_history_days(?,?,?,?,?,?)",

                new
Object[]{userID, startDate, endDate, typeOne, typeTwo, null},

                new
CalendarMapper());

    }

}

 

1.2 NamedParameterJdbcTemplate

public class CompanyDaoImpl extends NamedParameterJdbcDaoSupport
implements CompanyDao {

    public
List<Office> getCompanyOffices(long id) {

        Map<String,
Object> params = new HashMap<String, Object>();

        params.put("companyId",
id);

        // sqlResource.getSql()
is used to get sql string

        return
getNamedParameterJdbcTemplate().query(

sqlResource.getSql("getCompanyOfficesSQL"),
params, new OfficeRowMapper());

}

}

 

1.3 SimpleJdbcTemplate

public class CalendarDaoImpl extends SimpleJdbcDaoSupport
implements CalendarDao {

    public
List<Calendar> getLatestThirtyDays(Long userID, Date startDate, Date
endDate, int typeOne, int typeTwo) throws SQLException {

        return
super.getSimpleJdbcTemplate().getJdbcOperations().query("call proc_return_user_history_days(?,?,?,?,?,?)",

                new
Object[]{userID, startDate, endDate, typeOne, typeTwo, null},

                new
CalendarMapper());

    }

}

 

This
would be a bit overkilled to use SimpleJdbcTemplate, as the method getSimpleJdbcTemplate().getJdbcOperations()
is actually overlapping  with what getJdbcTemplate()
could provide.  However, by extends the SimpleJdbcDaoSupport,
the newly added feature in spring framework, you got both options to run your
DAO implementation with NamedParamter feature or not with this feature in the
same class.

1.4
RowMapper

The
CalenderMapper and OfficeRowMapper that used in the preceding code are all
extended from RowMapper interface, which is used by JdbcTemplate for mapping rows of a ResultSet on a per-row basis.
Implementations of this interface perform the actual work of mapping each row
to a result object, but don’t need to worry about exception handling. SQLExceptions will be caught and handled
by the calling JdbcTemplate.

public class OfficeRowMapper implements RowMapper {

    public Object mapRow(ResultSet
rs, int rowNum) throws SQLException {

        Office office =
new Office();

       
office.setId(rs.getLong("id"));

       
office.setCompanyId(rs.getLong("company_id"));

        return office;

    }

}          

 

1.5 StoreProcedure VS
SimpleJdbcCall

The StoredProcedure class is a superclass
for object abstractions of RDBMS stored procedures. This class is abstract, and
its various execute(..) methods have protected access, preventing use other
than through a subclass that offers tighter typing.

protected class VesselDetailsQuery extends StoredProcedure {

    public VesselDetailsQuery(DataSource
ds) {

        super(datasource,
"vessel_pkg.get_details");

        declareParameter(new
SqlParameter("id ", Types.NUMERIC));

       
declareParameter(new SqlOutParameter("data",
OracleTypes.CURSOR, new VesselRowMapper()));

       
declareParameter(new SqlParameter("username", Types.VARCHAR));

        compile();

    }

}

VesselDetailsQuery
is an inner class in the DAO class. It extends from StoredProcedure and
constructed with two variable: datasource and the procedure name;

SqlParameter
is used to declare the input parameter and SqlOutParameter is there for output
parameters. You can implement the RowMapper for the output like what we did
earlier.

SimpleJdbcCall takes advantage of metadata present in the database to look
up names of in and out parameters. This means that you don’t have to explicitly
declare parameters. You can of course still declare them if you prefer to do
that or if you have parameters that don’t have an automatic mapping to a Java
class like ARRAY or STRUCT parameters

public class JdbcActorDao implements ActorDao {

    private
SimpleJdbcTemplate simpleJdbcTemplate;

    private SimpleJdbcCall
procReadActor;

 

    public void setDataSource(DataSource
dataSource) {

       
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);

        this.procReadActor
=

                new
SimpleJdbcCall(dataSource)

                       
.withProcedureName("read_actor");

    }

 

    public Actor
readActor(Long id) {

        SqlParameterSource
in = new MapSqlParameterSource()

               
.addValue("in_id", id);

        Map out =
procReadActor.execute(in);

        Actor actor = new
Actor();

        actor.setId(id);

        actor.setFirstName((String)
out.get("out_first_name"));

       
actor.setLastName((String) out.get("out_last_name"));

        actor.setBirthDate((Date)
out.get("out_birth_date"));

        return actor;

    }

}

The execution of the call involves
creating an
SqlParameterSource containing the in parameter. It’s
important to match the name of the parameter declared in the stored procedure.
The case doesn’t have to match since we use metadata to determine how database
objects should be referred to – what you specify in your source for the stored
procedure is not necessarily the way it is stored in the database, some
databases transform names to all upper case while others use lower case or the
case as specified.

You can specify all the details explicitly
rather than relying on metadata. This will be necessary if the database we use
is not part of the supported databases.

    public void
setDataSource(DataSource dataSource) {

        JdbcTemplate jdbcTemplate
= new JdbcTemplate(dataSource);

        jdbcTemplate.setResultsMapCaseInsensitive(true);

        this.procReadActor
=

                new SimpleJdbcCall(jdbcTemplate)

                       
.withProcedureName("read_actor")

                       
.withoutProcedureColumnMetaDataAccess()

                       
.useInParameterNames("in_id")

                       
.declareParameters(

                               
new SqlParameter("in_id", Types.NUMERIC),

                               
new SqlOutParameter("out_first_name", Types.VARCHAR),

                               
new SqlOutParameter("out_last_name", Types.VARCHAR),

                                new
SqlOutParameter("out_birth_date", Types.DATE)

                        );

    }

 

 

  1. Object Relational Mapping (ORM) data access

Spring advocates strikingly simple
solutions for proper resource handling, namely IoC via templating; for example
infrastructure classes with callback interfaces, or applying AOP interceptors. Spring
also offers Hibernate and JDO support, consisting of a
HibernateTemplate / JdoTemplate analogous to JdbcTemplate, a  HibernateInterceptor / JdoInterceptor, and a Hibernate / JDO transaction
manager. All the individual data access features are usable on their own but
integrate nicely with Spring’s application context concept, providing XML-based
configuration and cross-referencing of plain JavaBean instances that don’t need
to be Spring-aware. In a typical Spring application, many important objects are
JavaBeans: data access templates, data access objects (that use the templates),
transaction managers, business services (that use the data access objects and transaction
managers), web view resolvers, web controllers (that use the business
services), and so on.

Implementing Spring-based DAOs

HibernateTemplate
will ensure that Session instances are properly opened and closed, and
automatically participate in transactions. The template instances are
thread-safe and reusable, they can thus be kept as instance variables of the
surrounding class. For more details
about how to access the data with HibernateTemplate, you can view my earlier
article at http://mybiblebook.spaces.live.com/blog/cns!8D4E97688DE1E193!460.entry

A callback implementation effectively can be used for any
Hibernate data access

public class ProductDaoImpl implements ProductDao {

 

    private
HibernateTemplate hibernateTemplate;

 

    public void
setSessionFactory(SessionFactory sessionFactory) {

       
this.hibernateTemplate = new HibernateTemplate(sessionFactory);

    }

 

    public Collection
loadProductsByCategory(final String category) throws DataAccessException {

        return
this.hibernateTemplate.execute(new HibernateCallback() {

 

            public Object
doInHibernate(Session session) {

                Criteria
criteria = session.createCriteria(Product.class);

               
criteria.add(Expression.eq("category", category));

               
criteria.setMaxResults(6);

                return
criteria.list();

            }

        };

    }

}

 

As
alternative to using Spring’s HibernateTemplate to implement DAOs, data
access code can also be written in a more traditional fashion, without wrapping
the Hibernate access code in a callback, while still respecting and participating
in Spring’s generic DataAccessException hierarchy. The advantage of such
direct Hibernate access code is that it allows any checked application
exception to be thrown within the data access code; contrast this to the HibernateTemplate
class which is restricted to throwing only unchecked exceptions within the
callback. Note that you can often defer the corresponding checks and the
throwing of application exceptions to after the callback, which still allows
working with HibernateTemplate.
In general, the HibernateTemplate class’ convenience methods are simpler
and more convenient for many scenarios.

public class HibernateProductDao extends HibernateDaoSupport
implements ProductDao {

    public Collection
loadProductsByCategory(String category) throws DataAccessException, MyException
{

        Session session =
getSession(false);

        try {

            Query query =
session.createQuery("from test.Product product where product.category=?");

           
query.setString(0, category);

            List result =
query.list();

            if (result ==
null) {

                throw new
MyException("No search results.");

            }

            return result;

        }

        catch (HibernateException
ex) {

            throw
convertHibernateAccessException(ex);

        }

    }

}

 

 Implementing
DAOs based on plain Hibernate 3 API

Hibernate
3 provides a feature called "contextual Sessions", where Hibernate
itself manages one current Session per transaction. This is roughly
equivalent to Spring’s synchronization of one Hibernate Session per
transaction. A corresponding DAO implementation looks like as follows, based on
the plain Hibernate AP

public class ProductDaoImpl implements ProductDao {

 

    private SessionFactory
sessionFactory;

 

    public void
setSessionFactory(SessionFactory sessionFactory) {

       
this.sessionFactory = sessionFactory;

    }

 

    public Collection
loadProductsByCategory(String category) {

        return
this.sessionFactory.getCurrentSession()

               
.createQuery("from test.Product product where
product.category=?")

                .setParameter(0,
category)

                .list();

    }

}

 

The
above DAO follows the Dependency Injection pattern: it fits nicely into a
Spring IoC container, just like it would if coded against Spring’s HibernateTemplate.
Of course, such a DAO can also be set up in plain Java (for example, in unit
tests): simply instantiate it and call setSessionFactory(..) with
the desired factory reference.

Reference:

http://static.springsource.org/spring/docs/2.5.x/reference/orm.html#orm-hibernate

http://static.springsource.org/spring/docs/2.5.x/reference/jdbc.html

 

Posted in Spring | 1 Comment