This blog is mainly about Java...

Saturday, September 27, 2008

How to enable second level caching in Hibernate

To enable second level caching in hibernate you can do it two ways:
One of them is by creating a namedQuery and put it as cacheable and then call the namedQuery.

In your Entity class type
@NamedQueries( { @NamedQuery(name = "findAllDistinctPersons", query = "SELECT DISTINCT p FROM Person p", hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"), @QueryHint(name = "org.hibernate.cacheRegion", value = "alkohol_register") }) })

Here I have created a NamedQuery called findAllDistinctPerons, and I put hints to be org.hibernate.cacheable as value true. Then you can if you want put a region.
This is optional. This basically means that you can define a region in your configuration file and define which part of the application should use this region.
You should do this if you want some region to be cacheable and some region not to be, but still want to reuse the object.

The other way is doing it when you are creating the query.
personList = entityManager.createQuery("SELECT DISTINCT p FROM " + Person.class.getName() + " p INNER JOIN p.personAdresseAdressetypes paa " +
"WHERE p.aktiv = " + getSearchAktiv() + " "
+ SearchHelper.getAdresseSearch(getSearchStringAdresse(),true, "paa")).setHint("org.hibernate.cacheable", true).getResultList();
Here you set the Hint directly on the query. Note that we are not defining any region here.

Seam, EJB and Hibernate useful tips

Here are some tips I have gathered during our Seam project.

@Create - Signals that this method should be called upon instantiation of the JavaBean.

@Stateless - Makes an EJB3 object

@Stateful - (SESSION) Caches in memory between server requests

@In - These variables will automatically be set by Seam through injection of the variables labeled by the annotation. i.e
String zipCode; (Behind the curtain: zipCode = request.getParameter("zipCode"); )

@Out - Same as above. This variable is also automatically set by Seam. Behind the curtain this variable is set in the HttpSession object.
( request.setAttribute("bestRestaurant", restaurant); )
What you also must remember is that if you want to use the outjected object, you only call it with its name. Not component name first.
i.e. If you stateful session bean is named @Name("someName") and you have a @Out String test, then when you use it in the view, you must write #{test} and not #{someName.test}
You can also say @Out(required = false) which means that the Object can be null. Otherwize it cannot.

@DataModel annotation exposes an attribute of type java.util.List to the JSF page as an instance of javax.faces.model.DataModel. This allows us to use the list in a JSF with clickable links for each row. The DataModel is made available in a session context variable named the variable name.

@PersistenceContext(type=EXTENDED)
EntityManager em;
This stateful bean has an EJB3 extended persistence context. The messages retrieved in the query remain in the managed state as long as the bean exists, so any subsequent method calls to the stateful bean can update them without needing to make any explicit call to the EntityManager.
Remember, its a good idea to clear the entitymanager once in a while if you do lots of searches

@Factory("messageList")
public void findMessages() {
messageList = em.createQuery("from Message msg order by msg.datetime desc").getResultList();
}
The first time we navigate to the JSP page, there will be no value in the messageList context variable. The @Factory annotation tells Seam to create an instance of MessageManagerBean and invoke the findMessages() method to initialize the value. We call findMessages() a factory method for messages.

@Remove
@Destroy
public void destroy() {}
All stateful session bean Seam components must have a method with no parameters marked @Remove that Seam uses to remove the stateful bean when the Seam context ends, and clean up any server-side state.

@NamedQuery(name="findProeveByAddress",
query = "SELECT p " +
"FROM Proeve p, PersonAdresseAdressetype paa "
+ "WHERE paa.person = p.person "
+ "AND (lower(p.person.fornavn) like :fornavn OR lower(p.person.mellomnavn) like :fornavn) "
+ "AND (lower(p.person.etternavn) like :etternavn) "
+ "AND (lower(p.person.foedselsnummer) like :foedselsnr) "
+ "AND (lower(paa.adresse.adressefelt1) like :adresse) "
+ "AND (lower(p.kommune.navn) like :kommunenavn) "
+ "order by p.person.etternavn, p.avlagt desc"
)

@Lob - Used for storing large string/byte objects. Becomes text or blob.

Join Fetch - Makes the fetch eager. Be careful using join fetch. What I mean with that is that you should only use fetch when you are going to call methods
on the object being fetched. Otherwize it will be a performance issue.

@Enumerated(EnumType.STRING) - Makes the enum as string in the database

@Observer("proeveSearchDirty")
public void clear() {
proeveSokList = null;
}
With the observer annotation you can call on methods by giving it a name from other seam components.
For instance you can raise event which will call the observer method after an persist is made

@RaiseEvent("proeveSearchDirty")
public String persist() {
return super.persist();
}

If you want to override the default "Successfully created/updated/removed" from Seam and your EntityHome objects, then you can either override the methods
in the Home class. i.e
@Override
public String getCreatedMessage() {
return "Saved";
}

@Override
public String getUpdatedMessage() {
return "Updated";
}

@Override
public String getDeletedMessage() {
return "Removed";
}
Or you can put it in the messages.properties file with the notation:
*EntityName*.created. For instance:
Adresse_created=Lagret
Adresse_updated=Oppdatert
Adresse_deleted=Slettet
The messages.properties will be the default even if you have overrided the methods in your Home objects.

Tuesday, September 2, 2008

Deleted entity passed to persist exception

Deleting an entity was seemingly more difficult than I thought.
I thought that I could just loop through a list, find the objects I wanted to delete and just delete them.

Look at this block of code:

List relasjoner = entityManager.createQuery(
"SELECT rel FROM " + Relasjon.class.getName() + " rel " + "WHERE rel.personA.personId = " + personb.getPersonId()
+ " OR rel.personB.personId = " + personb.getPersonId()).getResultList();

for (Relasjon rel : relasjoner) {
if((rel.getPersonB().getPersonId().equals(personb.getPersonId()) || rel.getPersonA().getPersonId().equals(personb.getPersonId()))
// Time to remove
log.debug("Going to remove this relasjon with id: " + rel.getRelasjonId());
entityManager.remove(rel);
}
}

The line entityManager.remove(rel) gave me a "Deleted entity passed to persist" exception with Relasjon<#null>
After some Googling I found out that I had to add these two lines before trying to remove the entity:

rel.getPersonA().getRelasjonsForPersonA().remove(rel);
rel.getPersonB().getRelasjonsForPersonB().remove(rel);


getRelasjonsForPersonA() is a Set of persons.
So I have to remove all the references to a Relasjon first before deleting the entity, and then no more exception.

Labels