Virtual Field Mapping

A virtual mapping is used to map data from one database field to more than one fields in a Java object model. A transfer operation is supposed to do the actual splitting and connecting work, while the results of this transfer are supposed to be delivered as a normal java.util.Map .

Signsoft intelliBO manages these virtual fields and ensures, that the values are stored correctly into the database.

Because there exist no clear mapping between a database field and a Java field, it is not feasible to query about virtual mapped fields. The querying feature of normal mapped fields is not supported.

The following example shows how to use virtual mappings. As container object we use the class Person , where an birth date consisting of the fields birthday, birthMonth and birthYear should be mapped to a single database field. In the database the values are stored as a string value in the format YYYY/MM/DD (e.g. 1969/04/02).

This gives us the following class:

public class Person 
{
  ...
  private int birthDay;
  private int birthMonth;
  private int birthYear;
  ...
  public int getBirthDay ()
  {
    return birthDay;
  }

  public int getBirthMonth ()
  {
    return birthMonth;
  }

  public int getBirthYear ()
  {
    return birthYear;
  }

  ...
  public void setBirthDay (int value)
  {
    birthDay = value;
  }

  public void setBirthMonth (int value)
  {
    birthMonth = value;
  }

  public void setBirthYear (int value)
  {
    birthYear = value;
  }

  ...
}
To map this for usage within Signsoft intelliBO, we define the fields birthDay, birthYear and birthMonth of class Person as persistent, with the virtual mapping type. In order to use virtual mapping, you have to specify at least the following:

These data has to be specified for each virtual mapping. Additionally you may specify data as for a normal direct field mapping, incl. a database table name and an SQL type. Because a transfer operation is used, there is no support for an exchange operation.

This is an example for JDO metadata with a virtual mapping:

<?xml version="1.0" encoding="UTF-8"?>
<jdo xmlns="http://java.sun.com/xml/ns/jdo/jdo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/jdo/jdo http://java.sun.com/xml/ns/jdo/jdo_2_0.xsd">

  <package name="com.signsoft.ibo.sample">
      ...
      <field default-fetch-group="false" name="birthDay" primary-key="false">
        <extension key="jdbc" vendor-name="ssibo">
          <extension key="mapping" value="virtual" vendor-name="ssibo">
            <extension key="field-name" value="FDATE" vendor-name="ssibo"/>
            <extension key="name" value="birthDate" vendor-name="ssibo"/>
            <extension key="field" value="day" vendor-name="ssibo"/>
            <extension key="transfer-operation" 
             value="com.signsoft.ibo.sample.transfer.DateTransferOperation" vendor-name="ssibo"/>
          </extension>
        </extension>
      </field>
      <field default-fetch-group="false" name="birthMonth" primary-key="false">
        <extension key="jdbc" vendor-name="ssibo">
          <extension key="mapping" value="virtual" vendor-name="ssibo">
            <extension key="field-name" value="FDATE" vendor-name="ssibo"/>
            <extension key="name" value="birthDate" vendor-name="ssibo"/>
            <extension key="field" value="month" vendor-name="ssibo"/>
            <extension key="transfer-operation"
             value="com.signsoft.ibo.sample.transfer.DateTransferOperation" vendor-name="ssibo"/>
          </extension>
        </extension>
      </field>
      <field default-fetch-group="false" name="birthYear" primary-key="false">
        <extension key="jdbc" vendor-name="ssibo">
          <extension key="mapping" value="virtual" vendor-name="ssibo">
            <extension key="field-name" value="FDATE" vendor-name="ssibo"/>
            <extension key="name" value="birthDate" vendor-name="ssibo"/>
            <extension key="field" value="year" vendor-name="ssibo"/>
            <extension key="transfer-operation"
             value="com.signsoft.ibo.sample.transfer.DateTransferOperation" vendor-name="ssibo"/>
          </extension>
        </extension>
      </field>
      ...
    </class>
  </package>
</jdo>
In order to use this virtual mapping, you still have to provide a transfer operation. This class implements the interface TransferOperation in package com.signsoft.ibo.jdbc and handles the conversion of database to java object values and back. It is supposed to return resp. handle a map with keys and values as specified in the JDO metadata. I.e. if the metadata specify that there is used a field "year", the transfer operation should be able to handle the MapEntry with the key "year".

The following shows an sample transfer operation:

import com.signsoft.ibo.jdbc.TransferOperation;
import java.util.*;
import com.signsoft.ibo.jdbc.*;

public class DateTransferOperation implements TransferOperation 
{
  public Object toDatabase(Map parm1) throws ExchangeException {
    if (parm1 == null) return null;

    Object o;
    int day = 0;
    o = parm1.get("day");
    if ((o != null) && (o instanceof Integer))
      day = ((Integer) o).intValue();

    int month = 0;
    o = parm1.get("month");
    if ((o != null) && (o instanceof Integer))
      month = ((Integer) o).intValue();

    int year = 0;
    o = parm1.get("year");
    if ((o != null) && (o instanceof Integer))
      year = ((Integer) o).intValue();

    StringBuffer sb = new StringBuffer();
    sb.append (year).append ("/").append(month).append("/").append(day);

    return sb.toString();
  }

  public Map fromDatabase(Object parm1) throws ExchangeException {
    if (parm1 == null) return null;
    if (!(parm1 instanceof String)) return null;

    StringTokenizer st = new StringTokenizer ((String) parm1, "/");

    int year = 0;
    if (st.hasMoreElements())
      year = Integer.parseInt ((String) st.nextElement());

    int month = 0;
    if (st.hasMoreElements())
      month = Integer.parseInt ((String) st.nextElement());

    int day = 0;
    if (st.hasMoreElements())
      day = Integer.parseInt ((String) st.nextElement());

    Map m = new HashMap();
    m.put("day", new Integer(day));
    m.put("month", new Integer(month));
    m.put("year", new Integer(year));

    return m;
  }
}
In your application, you may use virtual fields like any other direct mapped fields, e.g. like the following:
Person p = new Person();
p.setBirthDay (12);
p.setBirthMonth (3);
p.setBirthYear (1971);

pm.makePersistent(p);