Saturday, June 27, 2009

Hibernate Bi-Directional Many to Many mapping

There are times we have many to many relation between 2 objects (tables). The many to many relationship between 2 tables is defined by a third table. For example, suppose we 2 tables: “customer” and “service” and we say that a customer can be related to more than one service, we add a third table: “user_customer” that will hold the relations between customers and services.

Let’s summarize the structure of these 3 tables:

  • customer: This table holds customers. The fields in this table are:
    • customer_id (primary key)
    • first_name
    • last_name
  • service: This table holds list of services that a customer can receive. For example, cables TV customer can get the following services: Sports, Drama, News etc’. A customer can get more than one service. The fields in this table are:
    • service_id (primary key)
    • name
  • customer_service: This table defines the relation between customers and services. It connected users to services. It contains only the key from each table:
    • user_id
    • service_id

Note that “customer” and “service” tables used in this example doesn’t contain all the business data usually would have been used in the real world. This is to make things simpler.

Hibernate has a special annotation that helps us to define this relation: @ManyToMany. This is how this annotation is being used:

This is the Customer class representing the “customer” table:

package com.bashan.blog.hibernate;
import javax.persistence.*;
import java.util.Set;
@Entity
@Table(name = "customer")
public class Customer {
  @Id
  @Column(name = "customer_id")
  private Integer customerId;
  @Column(name = "first_name")
  private String firstName;
  @Column(name = "last_name")
  private String lastName;
  @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
  @JoinTable(name = "customer_service",
      joinColumns = {@JoinColumn(name = "customer_id")},
      inverseJoinColumns = @JoinColumn(name = "service_id"))
  private Set<Service> services;
  public Integer getCustomerId() {
    return customerId;
  }
  public void setCustomerId(Integer customerId) {
    this.customerId = customerId;
  }
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public String getLastName() {
    return lastName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

This is the Service class representing the “service” table:

package com.bashan.blog.hibernate;
import javax.persistence.*;
import java.util.Set;
@Entity
@Table(name = "service")
public class Service {
  @Id
  @Column(name = "service_id")
  private Integer serviceId;
  @Column(name = "name")
  private String name;
  @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
  @JoinTable(name = "customer_service",
      joinColumns = {@JoinColumn(name = "service_id")},
      inverseJoinColumns = @JoinColumn(name = "customer_id"))
  private Set<Customer> customers;
  public Integer getServiceId() {
    return serviceId;
  }
  public void setServiceId(Integer serviceId) {
    this.serviceId = serviceId;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

Not that there is no need to define any CustomerService object since the @ManyToMany annotation already contains this information in the “name” property of the @JoinTable.

The code for these 2 classes can be found in this link.

Hibernate Annotations CollectionOfElements to create a Map

There are times is it very comfortable using Map data structure to easily get elements by some key. For example, suppose we have in our database a “user” table, and we would like easily store and retrieve user properties.

Hibernate gives a nice solution to this issue, by using the annotation @CollectionOfElements. We will see a demonstration of using this annotation with our “user” table. Suppose we have 2 tables in out database:

  • user: Contains users information. The table contains the following fields:
    • user_id
    • first_name
    • last_name
    • password
  • user_property: Stores user properties. The tables contains the following fields:
    • user_id
    • key
    • value

The structure of these 2 tables is quite simple and straight forward, therefore, we will not broaden on it.

We would like that “user_property” will be a Map containing key and value pairs, rather than being a collection in “user”. We will use @CollectionOfElements annotation in order to achieve that. This is how our User class looks like:

package com.bashan.blog.hibernate;
import org.hibernate.annotations.CollectionOfElements;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Map;
@Entity
@Table(name = "user")
public class User implements Serializable {
  @Id
  @Column(name = "user_id")
  private Integer userId;
  @Column(name = "first_name")
  private String firstName;
  @Column(name = "last_name")
  private String last_name;
  @Column(name = "password")
  private String password;
  @CollectionOfElements
  @JoinTable(name = "user_property", joinColumns = @JoinColumn(name = "user_id"))
  @org.hibernate.annotations.MapKey(columns = {@Column(name = "key")})
  @Column(name = "value")
  private Map<String, String> properties;
  public Integer getUserId() {
    return userId;
  }
  public void setUserId(Integer userId) {
    this.userId = userId;
  }
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public String getLast_name() {
    return last_name;
  }
  public void setLast_name(String last_name) {
    this.last_name = last_name;
  }
  public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
  }
  public Map<String, String> getProperties() {
    return properties;
  }
  public void setProperties(Map<String, String> properties) {
    this.properties = properties;
  }
}
Note that no class exists for “user_properties” table. In this case the @CollectionOfElements mapping acts like in @ManyToMany mapping. The link to “user_properties” table is defined by the “name” property of @JoinTable. Also pay attention to the fact the @MapKey annotation that is used here belongs to “org.hibernate.annotations” and not to the “javax.persistence” package.

You can also download this class by pressing this link.

Saturday, June 13, 2009

Programtically compile Java source code

There are times we need to compile Java code from our application. For example, if we are dynamically generating some specific Java code to be packaged and sent to some customer.
Of course we can use "javac.exe" in order to achieve our goal. But executing "javac" is less programmatic way of doing the job.
Ever since Java 1.6 there are set of tools allowing us to compile Java sources directly from Java code. We will make use these tools in order to build a neat Java source code compiler, that will allow us to compile java files as well as java strings representing source code.
We will make use of the following classes supplied by the Java language:
  • StandardJavaFileManager: This class is used for adding classes and jars to the compiler as well as determining the output of the compiled classes.
  • JavaCompiler: This class is the actual compiler responsible of compiling all the the sources and other resources added by StandardJavaFileManager.
  • JavaCompiler.CompilationTask: This is the actual compilation task. The compilation takes place by calling it method: call.
  • SimpleJavaFileObject: We will extend this class in order to introduce the ability to add directly a String as a Java source code.
Out main class is called: JavaCodeCompiler. This is a wrapper to the Java compiler classes. This class introduces the following important methods:
  • addSource: This method adds all the Java classes under a given directory. It knows to handle directories and subdirectories as well. In addition, there is another overloaded addSource method, that allows adding a string represting a Java class. This is sometimes useful, when we would like to dynamically generate a Java source code.
  • compile: This methods does the actual compilation. It takes all the resources we added, compiles them and put the result in the output directory we defined in the class constructor.

This is how the JavaCodeCompiler looks like:

package com.bashan.blog.compile;
import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JavaCodeCompiler {
  private static final Pattern PATTERN_CLASS_NAME =
        Pattern.compile(".*?class\\s*?([a-zA-Z_][a-zA-Z0-9_]*)\\s*?", Pattern.DOTALL);
  private JavaCompiler compiler;
  private File dirOutClasses;
  private StandardJavaFileManager filemanager;
  protected List<File> fileSources;
  protected List<JavaFileObject> sources;
  public JavaCodeCompiler(File dirOutClasses) throws IOException {
    compiler = ToolProvider.getSystemJavaCompiler();
    filemanager = compiler.getStandardFileManager(null, null, null);
    this.dirOutClasses = dirOutClasses;
    fileSources = new ArrayList<File>();
    sources = new ArrayList<JavaFileObject>();
  }
  public void addSource(File dir) throws Exception {
    fileSources.clear();
    addDir(dir);
    sources = (List<JavaFileObject>) filemanager.
        getJavaFileObjects(fileSources.toArray(new File[fileSources.size()]));
  }
  private void addDir(File dir) {
    File[] files = dir.listFiles();
    for (File file : files) {
      if (file.isDirectory()) {
        addDir(file);
      } else if (file.getPath().endsWith(JavaFileObject.Kind.SOURCE.extension)) {
        fileSources.add(file);
      }
    }
  }
  private String getClassName(String code)
  {
    Matcher matcher = PATTERN_CLASS_NAME.matcher(code);
    if (matcher.find())
    {
      return matcher.group(1);
    }
    return null;
  }
  public void addSource(String code) {
    String className = getClassName(code);
    sources.add(new JavaSourceString(className, code));
  }
  public void compile() throws IOException, CompileException {
    try {
      dirOutClasses.mkdirs();
      filemanager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(dirOutClasses));
      JavaCompiler.CompilationTask task = compiler.getTask(null, filemanager, null, null, null, sources);
      if (!task.call()) {
        throw new CompileException("Failed compiling classes");
      }
    }
    finally {
      filemanager.close();
    }
  }
}

In addition to this class, there are 2 more classes used. One is: CompileException. This class is defined since the Java compilation task returns “true” or “false” as success/failure indication, an we would like to generate a more proper mechanism that will force user to deal with compilation failures. This is how this class looks:
package com.bashan.blog.compile;
public class CompileException extends Exception {
  public CompileException() {
  }
  public CompileException(String message) {
    super(message);
  }
  public CompileException(String message, Throwable cause) {
    super(message, cause);
  }
  public CompileException(Throwable cause) {
    super(cause);
  }
}
And the final class named: JavaSourceString extends the class SimpleJavaFileObject which supplied by Java. It gives the ability to add String Java source code. This is how it looks:
package com.bashan.blog.compile;
import javax.tools.SimpleJavaFileObject;
import java.net.URI;
public class JavaSourceString extends SimpleJavaFileObject {
  final String code;
  JavaSourceString(String name, String code) {
    super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
    this.code = code;
  }
  @Override
  public CharSequence getCharContent(boolean ignoreEncodingErrors) {
    return code;
  }
}

This is a basic Java Source code compiler. You can extend its capabilities further more, for example: allowing to add directly a Velocity template representing a dynamically created Java class.

You can download the classes in this post directly by using this link.