4

This is in continuation with this question I asked a yesterday. After going through various resources and consulting people, I was not able to find any JPA annotations supporting API, for mapping units of measurement. So, I decided to go with creating it myself.

Based on various patterns on Observations and Measurements described by Martin Fowler in his book - Analysis Patterns: Reusable Object Models, I tried to create a basic implementation to meet my needs. I've created two entities, Unit and Quantity, as below: -

Unit entity: -

@Entity
@Table(name = "unit")
public class Unit {

    @Id
    @Column(name = "symbol")
    private String symbol;

    @Column(name = "name")
    private String name;

    @Column(name = "multiplier")
    private Number multiplier;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "symbol")
    private Unit baseUnit;

    public Unit() {

    }

    public Unit(String symbol, String name, Number multiplier, Unit baseUnit) {
        this.symbol = symbol;
        this.name = name;
        this.multiplier = multiplier;
        this.baseUnit = baseUnit;
    }

        /** Getters and Setters **/
}

Quantity Entity: -

@Entity
@Table(name = "quantity")
public class Quantity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int quantityId;

    private Number amount;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "unit")
    private Unit unit;

    public Quantity() {

    }

    public Quantity(Number amount, Unit unit) {
        this.amount = amount;
        this.unit = unit;
    }

    /** Getters and Setters **/
}

And in Item entity, I'm having a reference of Quantity entity, to represent my unitOfIssue and unitOfPurchase. And then, wherever I've to deal with some quantity, I'll have this Quantity reference.

Now, here's the problem I'm facing now. Since Quantity class here will be an entity. So, we have to map it to a DB Table. So, the caveat here is, everytime I want to add some quantity, or update the quantity, an entry will go in the Quantity table first, and then it will be updated in the Item table. I think, the problem is pretty clear. We would have huge entry in the Quantity table, which clearly shows a bad design.

Can someone give an insight on the approach and what options I've while I implement this pattern in JPA? And how can I solve the problem?

Community
  • 1
  • 1
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • @NandkumarTekale.. Ah! Yes. I forgot that. I've been in this issue from the beginning of this week. :( – Rohit Jain Jan 11 '13 at 07:01
  • ..make Quantity @Embeddable in some larger Order object or something? – radai Jan 11 '13 at 07:02
  • @radai.. I thought Embeddable is used to create Composite key. How does it fit this situation? – Rohit Jain Jan 11 '13 at 07:03
  • @radai. So, shall I make my Quantity class Embeddable? And embed it in the Item entity. and what about Unit class? – Rohit Jain Jan 11 '13 at 07:08
  • @Rohit Jain : if i understand your design correctly there will be very few instances of the Unit class - one for each distinct unit you use (Kg, Meter, Volt, Cubic feet, etc). if you know them all in advance just make it into an Enum – radai Jan 11 '13 at 07:11
  • @radai.. That would also be a good idea. Will sure try out that. – Rohit Jain Jan 11 '13 at 07:14
  • @radai. Seems like an Embeddable annotated class cannot have reference to another entity. `Unit` here. :( – Rohit Jain Jan 11 '13 at 07:40
  • @Rohit Jain - then make both Unit and Quantity children of Order – radai Jan 11 '13 at 07:48
  • @RohitJain, I think you need to take a look at the [Value Object pattern](http://martinfowler.com/bliki/ValueObject.html) and [how it should be implemented in JPA](http://stackoverflow.com/a/7222576/3916). You're usually going to have to implement a custom UserType in Hibernate or the equivalent for your ORM. Obviously, you'll also need to revisit your entity design, since relationships are not encouraged for value objects. – Vineet Reynolds Jan 11 '13 at 09:09
  • @VineetReynolds.. Thanks Vineet. Will take a look at it. For now, I'm making my way out of this using `enum` for `Unit` and Quantity as Embeddable. And I'm seeing some light in this path. :) – Rohit Jain Jan 11 '13 at 09:32

3 Answers3

1

I would recommend this instead. Since units of measure are unlikely to change often, it is worth building them into the code. That way you can meaningfully use the values in the program itself.

It also makes the database more cohesive if it will ever be used anywhere else. You can also extend Quantity to be things LinearQuantity, ArealQuantity, VolumetricQuantity (etc.) to make sure someone isn't trying to buy 30 feet of oil.

@Embeddable
public class Quantity{

   public enum Unit {FEET,METERS,INCHES,MM}

   @Enumerated( value = EnumType.STRING)
   private Unit unit;

   private Number amount;

   public Quantity() {

   }
}


@Entity
Public Class PurchaseOrder
{
    @Embedded
    @AttributeOverrides({
      @AttributeOverride(name="unit", column=@Column(name="UNIT")),
      @AttributeOverride(name="amount", column=@Column(name="AMOUNT"))
    })
    private Quantity quantity;
    ....

}
Mark Robinson
  • 3,135
  • 1
  • 22
  • 37
  • 1
    Well I've also used this approach only. However, I'll look into your suggestion of differentiating different kinds of Quantity later on. Thanks :) – Rohit Jain Jan 12 '13 at 12:26
0
  • You need a master table of units which will have all the units as they are fixed so you can create simple script to populate it.
  • For Quantity I will not recommened a seperate entity it can be property to transaction table which can simply establish relation between Item its quantity and unit and you can do that with @OneToOne with Unit Entity.

Here is the sample example

@Entity
Public Class PurchaseOrder
{
@OneToOne
private Unit unitOfPurchase;
@OneToOne
    private Unit unitOfIsuse;

-- Quanity number here ---
}

You should use Cascade very carefully for master tables.

Amit Deshpande
  • 19,001
  • 4
  • 46
  • 72
  • @AmitD.. Actually, I get very confused, when it comes to using ManyToOne. In this case, I think, since many `PurchaseOrder` will have the same `Unit`, so, shouldn't the mapping be `ManyToOne`? – Rohit Jain Jan 11 '13 at 08:14
  • @RohitJain Yes. In this case it can be `@ManyToOne` but only unidirectional relation. – Amit Deshpande Jan 11 '13 at 09:00
0

Check out this JPA library for the popular JScience project: JScience-JPA

Based on similar support for Java Monetary types (JSR 354) we also plan to add something similar for JSR 363

Werner Keil
  • 592
  • 5
  • 12
  • The JScience-JPA link is broken / leads to a closed site. – Hervian Dec 07 '17 at 11:52
  • Yes, java.net itself closed many months ago. Although the maintainer of JScience-JPA was an EG member of JSR 363, the project itsels has nothing to do with JSR 363, so we did not carry if over like other artifacts. However, https://github.com/krichter722/jscience-amount-jpa looks like a more recent GitHub effort to rescue it, so please give it a try. – Werner Keil Dec 07 '17 at 12:01
  • @WernerKeil The project was only a reproducer for an issue which I'm deleting right now. – Kalle Richter Sep 18 '22 at 13:36
  • Thanks, what are you using now, Jadira or is there a use case for something under https://github.com/unitsofmeasurement/uom-lib? – Werner Keil Sep 19 '22 at 15:55