Monthly Archives: January 2006

ManyToMany relationships with Java Persistence (JSR-220)

The scenario is as following: each table can have many reservations, and each reservation can span many tables. Here is our first trial:

// Table.java
@ManyToMany(
  targetEntity=Reservation.class
)
public List getReservations() {..}

// Reservation.java
@ManyToMany(
  targetEntity=Table.class
)
public List getTables() {..}

We end up with linktables tables_reservations and reservations_tables. This is not what we want. It shouldn’t be possible to remove a table when there are still reservations related to that table. We change our code so that table becomes the owner of the relationship.

// Table.java
@ManyToMany(
  targetEntity=Reservation.class,
  mappedBy="tables"
)
public List getReservations() {..}

Allright, now we only have the linktable reservations_tables. Exactly the same as we experienced with OneToMany relationships the cascading persist works when we persist a child (reservation). If we want the cascading persist to work when we persist a parent (table) we have to make sure that the child (reservation) has the parent set. An important difference is that we can’t enforce this thus i advise not to use the cascade attribute on the owner (table).

// Table.java
@ManyToMany(
  targetEntity=Reservation.class,
  mappedBy="tables"
)
public List getReservations() {..}

// Reservation.java
@ManyToMany(
  targetEntity=Table.class,
  cascade=CascadeType.PERSIST
)
public List getTables() {..}

We decide to use the Hibernate Validator classes to enforce that a reservation is associated with at least one table. For some reason the SizeValidator returns true if the value is null thus we also add a NotNullValidator:

// Reservation.java
import org.hibernate.validator.*;
@ManyToMany(
  targetEntity=Table.class,
  cascade=CascadeType.PERSIST
)
@NotNull
@Size(min=1)
public List getTables() { .. }

Ok, this allows us to enforce that reservations are always associated with at least one table. We decide to allow cascading persist on the parent.

@ManyToMany(
  targetEntity=Reservation.class,
  mappedBy="tables",
  cascade=CascadeType.PERSIST
)
public List getReservations() {..}

OneToMany relationships with Java Persistence (JSR 220)

Let’s experiment a bit with Hibernate. We want to model the tables employee and shift. Each employee has multiple shifts (and each shift belongs to an employee). We want an employee table and a shift table (which has the employee_id as a foreign key).

// Employee.java
@OneToMany(targetEntity=Shift.class) public List getShifts() {...}
// Shift.java
@ManyToOne public Employee getEmployee() {...}

The code above generates the following tables: employee, employee_shift and shift. Time to read the documentation and discover the mappedBy attribute.

// Employee.java
@OneToMany(targetEntity=Shift.class, mappedBy="employee") public List getShifts() {...}

Ok, now we get the tables we want. Let’s remove an employee that has shifts referencing him. An exception is thrown because of a foreign key constraint.

// Employee.java
@OneToMany(targetEntity=Shift.class, mappedBy="employee", cascade=CascadeType.REMOVE) public List getShifts() {...}

Ok, now we have cascading deletes working. Let’s try cascading inserts:

// Employee.java
@OneToMany(targetEntity=Shift.class, mappedBy="employee", cascade=CascadeType.ALL) public List getShifts() {...}
// Main.java
Employee employee = new Employee();
Shift shift = new Shift();
Vector shifts = new Vector();
shifts.add(shift);
employee.setShifts(shifts);
em.persist(employee);

With the code above the foreign key of shift will NULL instead of the employee_id. According to the documentation it is expected behaviour that cascading inserts only work when the children are saved.

// Employee.java
@OneToMany(targetEntity=Shift.class, mappedBy="employee", cascade=CascadeType.ALL) public List getShifts() {...}
// Main.java
Employee employee = new Employee();
Shift shift = new Shift();
shift.setEmployee(employee);
Vector shifts = new Vector();
shifts.add(shift);
employee.setShifts(shifts);
em.persist(employee);

It appears we can get cascading inserts to work as long as we don’t forget to set the employee. We add a constraint so employee can’t be null.

// Shift.java
@ManyToOne @Column(nullable=false) public Employee getEmployee() {...}
// Main.java
Shift shift = new Shift();
em.persist(shift);

WTF? I can persist a NULL reference anyway? The value for employee_id is allowed to be NULL? Have we found a bug? Let’s check the documentation:

// Shift.java
@ManyToOne @JoinColumn(nullable=false) public Employee getEmployee() {...}
// Main.java
Shift shift = new Shift();
em.persist(shift);

Ok, now are getting somewhere. And exception is thrown if we try to persist a shift that references to an employee that doesn’t exist in the database yet.

// Shift.java
@ManyToOne(cascade=CascadeType.PERSIST) @JoinColumn(nullable=false) public Employee getEmployee() {...}

CascadeType.ALL means that i want to cascade events on ALL events. My only requirements where that i cascade on PERSIST (insert) and on REMOVE (delete). So i need to change my annotation as following:

// Employee.java
@OneToMany(
  targetEntity=Shift.class,
  mappedBy="employee",
  cascade={
    CascadeType.PERSIST,
    CascadeType.REMOVE
  }
)
public List getShifts() {return shifts;}

Setting up an SSH tunnel

On the machine example there is a (tcp/ip) program listening on port 12345. The protocol it talks is some plaintext language. I want to talk with it, but i don’t want others to know what i’m sending to it. I’m lucky enough to have remote access to that machine via ssh. I setup a tunnel with the following command:

ssh -N -L 12345:example:12345 timvw@example

Now my program can connect to localhost:12345 and ssh will make sure that it ends up at example.:12345 without others being able to see the actual data :) For windows users i suggest that you take a look at Plink.

Select the first 50 words of an article

I’m cleaning up my code snippets and i found the following little trick in one of them that i’ve removed. Assuming that different words are separated by spaces we can use SUBSTRING_INDEX as following:

SELECT SUBSTRING_INDEX(body,' ',50) AS dn FROM mytable

Accept posted XML data

I remember that i’ve spent a lot of time finding something that allowed me to accept the posted XML data. The solution was very simple:

$data = file_get_contents('php://input');

Format sourcecode with VIM

I once read an article that explained how i could make Vim format my code but i forgot what the command was. Many websearches for “vim indent code” later i had the luminous idea to search for “vim format code” instead. The first site i visited already had the answer i had been looking for: “gg=G”. Simple as that :)

Brainteaser

Earlier today Chung Leong, an intelligent regular at comp.lang.php, posted a little brainteaser:

The two functions in the example below behave differently. The difference is easy to spot, of ocurse. The challenge is correctly explaining why this is so. Why does the second function
seemingly corrupt the cloned copy of an object?

<?php
// uncomment the clone operator for PHP 5

function Bobcat(&$obj) {
        $clone = /* clone */ $obj;
        $obj->attributes['Length'] = 0;
        $obj->data = "";
        return $clone;
}

function BritneySpear(&$obj) {
        $attr =& $obj->attributes;
        $clone = /* clone */ $obj;
        $obj->attributes['Length'] = 0;
        $obj->data = "";
        return $clone;
}

$data = "This is a test";
$obj1->attributes = array('Length' => strlen($data));
$obj1->data = $data;
$clone1 = Bobcat($obj1);
print_r($clone1);

$obj2->attributes = array('Length' => strlen($data));
$obj2->data = $data;
$clone2 = BritneySpear($obj2);
print_r($clone2);
?>
Result:

stdClass Object
(
    [attributes] => Array
        (
            [Length] => 14
        )

    [data] => This is a test
)
stdClass Object
(
    [attributes] => Array
        (
            [Length] => 0
        )

    [data] => This is a test
)

It took me fifteen minutes to figure out the source of this mysterious behaviour, but it took me a couple of hours to come up with the following explanation: After $attr =& $obj->attributes in the BritneySpear function the container that holds this variables has is_ref=1. Any properties that are references to other variables, will remain references when $obj is copied into $clone as explained in Object cloning.

If you want to know more about references i can advise you to read PHP References by Derick Rethans.

How private is private really?

Today i ended up at private properties exposed (Apparently it’s also used by PHPUnit)

class foo {
 private $bar = 42;
}

$obj = new foo;
$propname="\0foo\0bar";
$a = (array) $obj;
echo $a[$propname];

Restore from a MySQL dumpfile

If you are a longtime user of mysqldump you may have experienced that restoring with mysql < dumpfile doesn’t always work because of referential problems. Here is a little script that takes care of it: mysql-restore.txt.