Quantcast
Channel: Planet Apache
Viewing all articles
Browse latest Browse all 9364

Christian Grobmeier: Using Apache Cayenne with Apache Wicket

$
0
0

I have recently figured out how cool Apache Cayenne is. It is easy to use and has many benefits compared to the king of ORMs Hibernate. While I was setting up my Apache Wicket project I thought about the best way of integrating it. In short, the easiest solution is to use the Spring features of wicket and create a SpringBean with Cayennes datacontext. And here is the long version. My examples use the current Wicket trunk version which will be released as Wicket 1.5 somewhere in future.

First you’ll need to activate the Spring features in Wicket. This is shown in many tutorials out there, so I’ll keep it short. In your applications init() do:

getComponentInstantiationListeners().add(new SpringComponentInjector(this));

Then you’ll need to create a applicationContext.xml file in your src/main/webapp/WEB-INF directory. This is used by Spring to create all your Spring-beans. I have already added the SpringBean for Cayenne:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

	<bean id="cayenneConnector" class="de.grobmeier.persistence.CayenneConnector" />
</beans>

For the sake of completeness, Wicket 1.5 uses Spring 2.5.6. Please refer to this docs if you need more instructions.

My connector class (feel free to give it a better name) connects to Cayenne. Make sure you have saved cayenne.xml, your cayenne map and your drivers xml in src/main/resources. Please don’t forget to add your JDBC driver to your pom.xml and/or to your classpath.

import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.access.DataContext;

public class CayenneConnector {
    private ObjectContext objectContext = null;

    public CayenneConnector() {
        this.objectContext = DataContext.createDataContext();
    }

    public ObjectContext getObjectContext() {
        return objectContext;
    }
}

Since Spring creates only singletons by default, only one ObjectContext is created. If you have more than one domains, you need to parametrize this bean. This should be done in the applicationContext.xml.

Then you are ready to go – here is a sample DataProvider.

public class UserDataProvider implements IDataProvider<User> {
    @SpringBean(name = "cayenneConnector")
    private CayenneConnector connector;

I would like to return all my stored users therefore the name is UserDataProvider. You’ll need to tell Wicket to inject a SpringBean to this object – this can be done with the @SpringBean annotation.

    public UserDataProvider() {
        Injector.get().inject(this);
    }

Unfortunately you’ll need to add the above call to the constructor. Wicket needs to know the classes to inject. I would be more cool if Wicket would look up all the SpringBean annotations by default. However, it works.

    @SuppressWarnings("unchecked")
    public Iterator<? extends User> iterator(int first, int count) {
        SelectQuery query = new SelectQuery(User.class);
        query.setFetchLimit(count);
        query.setFetchOffset(first);
        List<User> result = connector.getObjectContext().performQuery(query);
        return result.iterator();
    }

Here we go – i prepared a SelectQuery and used the paging values from Wicket. The connector returns the ObjectContext, the query is done and an Iterator on the results is returned. The SelectQuery could be used as a member variable to avoid multiple instances.

    public IModel<User> model(User object) {
        return new LoadableDetachableModel<User>(object) {
            @Override
            protected User load() {
                // object has already been loaded through the constructor
                return this.getObject();
            }
        };
    }

This is not very nice, but will do the trick. I need to return a Wicket Model filled with my User. I have choosen the LoadableDetachableModel. It’s being filled at construction level. Therefore my load() method has nothing to do. You could do it in another way, for example you could move the complete Select-code into the load() method. This is the original intention. In my case this is enough and more flexible.

    public int size() {
        CountQuery<User> query = new CountQuery<User>(User.class);
        Map row = (Map) connector.getObjectContext().performQuery(query).get(0);
        return Integer.parseInt(row.get("C").toString());
    }

Please have in mind, Wicket will first call the size() method and cancel the process if the is 0 returned. I have created my own CountQuery to perform a SELECT COUNT(*). This is one of the less “not so nice things” with Cayenne. You’ll need to use the SQLTemplate and write a few lines code to create a count(*). However, thanks to generics this has become trivial.

That’s it- with the model builder gui from Cayenne not only this integration is a trivial task. Modelling your application was never more fun.


Viewing all articles
Browse latest Browse all 9364

Trending Articles