Oracle getTime() keep milliseconds - java

We have a Spring + Hibernate Java 7 web-application. We currently use .hbm.xml Hibernate files (instead of the more common known annotations) as mapping between the Java entities and database tables.
One of our Java entity classes has a java.util.Date with millisecond precision:
public class SomeEntity extends AbstractEntity{
...
private java.util.Date someDate; // with ms precision
...
}
In our .hbm.xml file we have the following for this date:
...
<hibernate-mapping>
<class name="com.namespace.Entity" table="entity" batch-size="10">
...
<property name="SomeDate" column="somedate" type="com.namespace.CustomDateType" />
...
Which is linked to a database table with the following row:
SOMEDATE TIMESTAMP(2) NULLABLE=Yes
Our CustomDateType class:
package com.namespace.type;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Date;
import org.hibernate.usertype.UserType;
public class CustomDateType implements UserType {
private static final int[] SQL_TYPES = { Types.TIMESTAMP };
#Override
public Object assemble(final Serializable serializable, final Object o) {
return deepCopy(serializable);
}
#Override
public Object deepCopy(final Object value) {
return value;
}
#Override
public Serializable disassemble(final Object o) {
return (Serializable) deepCopy(o);
}
#Override
public boolean equals(final Object x, final Object y) {
if (x == y) {
return true;
} else if ((x == null) || (y == null)) {
return false;
}
return x.equals(y);
}
#Override
public int hashCode(final Object o) {
return ((CustomDateType) o).hashCode();
}
#Override
public boolean isMutable() {
return false;
}
#Override
public Object nullSafeGet(final ResultSet resultSet, final String[] names, final Object owner) throws SQLException {
Date result = null;
final Timestamp timestamp = resultSet.getTimestamp(names[0]); // This is where the problem is
if (!resultSet.wasNull()) {
result = new Date(timestamp.getTime());
}
return result;
}
#Override
public void nullSafeSet(final PreparedStatement statement, final Object value, final int index) throws SQLException {
final Date dateToSet = (Date) value;
statement.setTimestamp(index, dateToSet == null ? null : new Timestamp(dateToSet.getTime()));
}
#Override
public Object replace(final Object o, final Object o1, final Object o2) {
return o;
}
#Override
public Class<Date> returnedClass() {
return Date.class;
}
#Override
public int[] sqlTypes() {
return SQL_TYPES;
}
}
When debugging someDate does indeed have milliseconds in the Java class. But, in the CustomDateType#nullSafeGet method the resulting TimeStamp doesn't have any milliseconds anymore.
The resultSet parameter is the oracle.jdbc.driver.ForwardOnlyResultSet, which continues into oracle.jdbc.driver.T4CPreparedStatement, which continues into the class & method where the core of the problem is: oracle.jdbc.driver.DateTimeCommonAccessor#getTime(int, Calendar)
In this method the milliseconds are stripped and you are left with second-precision.
Is there any way to keep the milliseconds in this situation? How to save millisecond precision when going from java.util.Date to database TIMESTAMP-typed column?
NOTE: The milliseconds are already gone before going into the database table. If I put a breakpoint at final Timestamp timestamp = resultSet.getTimestamp(names[0]); and step over, the resulting timestamp will have no milliseconds anymore.
EDIT: The contents of the hava.util.Date object and java.sql.Timestamp objects during debugging:
The fraction and millis are both removed and put to 0..

A java.sql.Timestamp stores second precision in the java.util.Date fastTime field (instead of milliseconds for a normal java.util.Date), and it has a separate nanos field for sub-second precision. When you call java.sql.Timestamp.getTime() it recombines these two into a millisecond precision value.
As you have declared your field as TIMESTAMP(2), it means you only have 2 decimals of sub-second precision. If you try to store 00:00:00.001, what actually gets stored is 00:00:00:00, and that is also what you retrieve again. In other words, you need to increase precision by declaring the field as TIMESTAMP(3) (or even higher, up to 9 is supported), or you need to live with the reduced precision of two decimals.
Note that if you use an higher than millisecond precision (so more than 3 decimals), you can only get that full precision using java.sql.Timestamp.getNanos(), which contains the full sub-second precision (the nanos are only the sub second fraction and should always be less than 1 second), or by converting the Timestamp to a java.time.LocalDateTime.

Related

How to do different types of object state validation

I need to provide records to the caller from one or two different data sources and either within a specified date range or year range.
My dilemma is should I use overloaded methods or a Request object with state validation logic.
So either:
public List<Record> getRecords (Date fromDate, Date toDate, boolean dataSourceARequired, boolean dataSourceBRequired)
public List<Record> getRecords (int fromYear, int toYear, boolean dataSourceARequired, boolean dataSourceBRequired)
or something like this:
public List<Record> getRecords(Request request)
where Request would look something like:
public class Request{
private final Date fromDate;
private final Date toDate;
private final int fromYear;
private final int toYear;
private final boolean dataSourceARequired;
private final boolean dataSourceBRequired;
public Request(Date fromDate, Date toDate, boolean dataSourceARequired, boolean dataSourceBRequired){
if (fromDate == null) {
throw new IllegalArgumentException("fromDate can't be null");
}
if (toDate == null) {
throw new IllegalArgumentException("toDate can't be null");
}
if (!dataSourceARequired && !dataSourceBRequired){
throw new IllegalStateException ("No data source requested");
}
if (fromDate.after(toDate)){
throw new IllegalStateException ("startDate can't be after endDate");
}
this.fromDate = fromDate;
this.toDate = toDate;
this.dataSourceARequired = dataSourceARequired;
this.dataSourceBRequired = dataSourceBRequired;
this.fromYear = -1;
this.toYear = -1;
}
public Request(int fromYear, int toYear, boolean dataSourceARequired, boolean dataSourceBRequired){
if (fromYear > toYear) {
throw new IllegalArgumentException("fromYear can't be greater than toYear");
}
if (!dataSourceARequired && !dataSourceBRequired){
throw new IllegalStateException ("No data source requested");
}
this.dataSourceARequired = dataSourceARequired;
this.dataSourceBRequired = dataSourceBRequired;
this.fromYear = fromYear;
this.toYear = toYear;
this.fromDate = null;
this.toDate = null;
}
}
Or is there another way?
You should not use the second case as it breaks the rule that every class should have a single well-defined responsibility. Here, your class is responsible for both detailed date ranges and year date ranges. If you added more criteria this class would grow into something monstrous.
So you can use the first method and many people do.
If you want to create classes to encapsulate request data you should create a base abstract class or interface and then have a different type of request subclass for each type of criteria that you could use. For example:
public interface Request {
execute();
}
public class YearRangeRequest implements Request {
int fromYear;
int toYear;
public execute();
... etc
Another solution: Use a DateRange class with two constructors:
public class DateRange {
final Date start;
final Date end;
public DateRange(Date start, Date end) {
this.start = start;
this.end = end;
}
public DateRange(int startYear, int endYear) {
this(yearBegining(startYear), yearBegining(endYear));
}
...
}

Java MyBatis Enum string value

I feel like this is a simple problem, but none of the things i tried work for me. I have an enum, the reason i have string constructor is because Java doesn't allow enum to be numerical..I tried AA, AB, 2C directly without string constructor but that gives an error. Note that for the existing enum i am adding C("2C").
public enum TestEnum{
AA("AA"), AB("AB"), C("2C");
private String display;
private TestEnum( String display ) {
this.display = display;
}
public String toString() {
return display;
}
public String getDisplay() {
return display;
}
public void setDisplay( String display ) {
this.display = display;
}
public String getName() {
return display;
}
Now i have a mybatis mapper which does a merge this is existing and one of the param to the mapper is TestEnum. Until now this worked fine since enum value and string value are same, but i added C("2C"). Now i want to insert 2C to the table using mybaits, but it always inserts C.
merge into text t
using (select #{id} as id from dual) d on (d.id = t.id)
when matched then
update set
appId = #{applId},
src = #{testEnum}
testEnum inserts C, so i changed that to #{testEnum.toString()} which gave me a there is no getter for property name toString() error. I tried #{testEnum.display} and #{testEnum.name} both of them still inserts C whereas i want it to insert 2C. Do you guys know an easier way of handling this?
I don't want to change the model object to pass String rather than TestEnum because this object is being used in many places.Is there a way this can be done in the mybatis mapper without changing model object?
Thanks for your help :)
What you need is a TypeHandler
First, add a static method to your TestEnum to return a TestEnum given a display string:
public static TestEnum fromDisplay(String display){
for (TestEnum v : TestEnum.values()){
if (v.getDisplay().equals(display)){
return v;
}
}
return null;
}
Then use it to create your TypeHandler:
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
public class TestEnumTypeHandler extends BaseTypeHandler<TestEnum> {
#Override
public void setNonNullParameter(PreparedStatement ps, int i, TestEnum parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter.getDisplay());
}
#Override
public TestEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
return TestEnum.fromDisplay(rs.getString(columnName));
}
#Override
public TestEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return TestEnum.fromDisplay(rs.getString(columnIndex));
}
#Override
public TestEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return TestEnum.fromDisplay(cs.getString(columnIndex));
}
}
Finally, register your TypeHandler in your mybatis xml:
<typeHandlers>
<typeHandler handler="blah.blah.TestEnumTypeHandler "/>
</typeHandlers>
In addition to #Malt Answer:
The reason why what you are trying doesn't works it's the MyBatis EnumTypeHandler by default sets name() value of the method and is marked with final so you cannot override it:
EnumTypeHandler.class (Line 38 to 44):
#Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
if (jdbcType == null) {
ps.setString(i, parameter.name());
} else {
ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
}
}
Otherwise, the enum is created from the method valueOf(type, name) which also uses the name of the enum.
#Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String s = rs.getString(columnIndex);
return s == null ? null : Enum.valueOf(type, s);
}
#Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String s = cs.getString(columnIndex);
return s == null ? null : Enum.valueOf(type, s);
}
So definitely, you need to use a typeHandler specific to handle your enum which has a specific behaviour, but I would extends directly EnumTypeHandler in specific enum type handlers, instead of BaseTypeHandler (Malt answer), because you could reuse some functionality (not in your case, but maybe in others) so it handles a general enum behaviour.
You do not need to write any custom TypeHandler if you want to insert the value of your Enum.
The only one thing you need to do is to specify the getter method's name in your MyBatis insert.
Example:
SQL:
CREATE TABLE demo
(
id BIGINT,
value VARCHAR(10),
status CHAR(1)
);
MyBatis mapper:
#Update("UPDATE demo SET status = #{status.value} WHERE id= #{uuid}")
long updateStatus(#Param("status") Status status, #Param("uuid") String uuid);
And the Java Enum:
public enum Status {
ACTIVE("A"),
INACTIVE("I");
Status(final String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
In your case you can use src = #{testEnum.display} in your SQL.

Apache CXF how to handle date conversion xsd:date to java.util.Date without timezone

Here is my problem with CXF. I have a SOAP1.2 service written in CXF. The service isn't complicated, it basically puts an XML int DB (Oracle 11.x). All the dates in WSDL are defined as xsd:date.
I have following jaxb bindings defined
<jxb:javaType name="java.util.Date" xmlType="xsd:date"
parseMethod="org.apache.cxf.xjc.runtime.DataTypeAdapter.parseDate"
printMethod="org.apache.cxf.xjc.runtime.DataTypeAdapter.printDate" />
And the Adapter class which utilizes these is as follows
public class Adapter2
extends XmlAdapter<String, Date>
{
public Date unmarshal(String value) {
return (org.apache.cxf.xjc.runtime.DataTypeAdapter.parseDate(value));
}
public String marshal(Date value) {
return (org.apache.cxf.xjc.runtime.DataTypeAdapter.printDate(value));
}
}
Since cxf..runtime.DataTypeAdapter always yields java.util.Date, there is always a time added which in turn problem for the Oracle, because Oracle's native procedure for xml validation yields error when it encounters date with time. (Change DB settings is not an option).
What libraries/classes can use to marshal/unmrashal xsd:date to dates without a time? Or do I have to write my own class extending the XmlAdapter?
I've came up with this
import java.util.Date;
import java.text.SimpleDateFormat;
import org.apache.cxf.xjc.runtime.DataTypeAdapter;
public class DateTypeAdapterWrapper {
private static final String DATE_FORMAT = "yyyy-MM-dd";
public static Date parseDate(String value) {
return (DataTypeAdapter.parseDate(value));
}
public static String printDate(Date value) {
String tmp = DataTypeAdapter.printDate(value);
if(tmp == null) {
return null;
}
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
return dateFormat.format(value);
}
}
No doubt, there is a better to this, but here it is.

Getting the next time in a Java List

My logic is eluding me on this one, I'm hoping someone has some code I can learn from.
In java I have a List of custom objects, one of the members of this object is date/time of a certain event.
I need to be able to find the next time in the List from the current/local time.
Ie, The local time is 6pm
The list contains:
1pm, 3pm, 4pm, 7pm, 10pm
I need a method to basically pull out 7pm, as the next event time to process.
Any suggestions, directions appreciated, thx
You can do it by assuming the first dateTime object in the list to be the closest initially and then iterating to find if any other dateTime from the list is more closer to your refernce dateTime.Something like this:
import java.util.ArrayList;
import java.util.List;
import org.joda.time.DateTime;
public class test {
public static void main(String[] args) {
CustomData cd1 = new CustomData("data1", new DateTime(100000));
CustomData cd2 = new CustomData("data2", new DateTime(200000));
CustomData cd3 = new CustomData("data3", new DateTime(300000));
CustomData cd4 = new CustomData("data4", new DateTime(400000));
List<CustomData> dataList = new ArrayList<CustomData>();
dataList.add(cd1);
dataList.add(cd2);
dataList.add(cd3);
dataList.add(cd4);
DateTime closestDate=dataList.get(0).getDateTime(); //initially assume first dateTime to be the closest
for(CustomData cd:dataList){
if(cd!=null && cd.getDateTime()!=null && cd.getDateTime().isBefore(closestDate.getMillis()) && cd.getDateTime().isAfter(DateTime.now())){
/*if the date time is before the closest date and after the reference DateTime you are comparing(in this case DateTime.now()) update the reference of closestDate */
closestDate=cd.getDateTime();
}
}
System.out.println(closestDate);
}
}
Also for reference the CustomData class:
import org.joda.time.DateTime;
public class CustomData {
private String name;
private DateTime dateTime;
public CustomData(String name, DateTime dateTime) {
this.name = name;
this.dateTime = dateTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DateTime getDateTime() {
return dateTime;
}
public void setDateTime(DateTime dateTime) {
this.dateTime = dateTime;
}
}
Assuming that:
You have a class Event which has a time property,
the time property is of type java.time.LocalTime or something else which either has a natural order (like java.util.Date) or for which you can easily provide a Comparator,
you have your events in a java.util.List<Event> which is unsorted,
your class Event defines a natural order according to time (implements Comparable<Event>) or you provide a Comparator<Event> which compares Event objects by their time property,
basically the relevant essentials of class Event being this,
import java.time.LocalTime;
public class Event implements Comparable<Event> {
LocalTime time;
#Override
public int compareTo(final Event o) {
return time.compareTo(o.time);
}
}
or
import java.time.LocalTime;
public class Event {
LocalTime time;
public static final Comparator<Event> byTime = new Comparator<Event>() {
#Override
public int compare(final Event o1, final Event o2) {
return o1.time.compareTo(o2.time);
}
};
}
then you could use one of the following ways to get the event you're looking for (and there are certainly many more ways):
You could iterate through the List with a for-each loop (comparative linear search).
You could stream() your list, filter() all events after that time, sort() them, and take the first.
You could put the events in a TreeMap<LocalTime, Event> and ask the TreeMap for the ceilingKey().
Which of these solutions is best depends on how you actually store your data. If access on List<Event> is frequently done based on the time property, you might want to permanently keep a TreeMap<LocalTime, Event> in your application. In that case, the third solution is best. If it's done rarely, I'd use the Streams solution. The first solution is the naive primitive approach, and although we've been coding that way for decades, I don't like it.
Here are these three solutions:
public static Event getNextEventUsingComparison(final List<Event> unsortedEvents, final LocalTime timestamp) {
Event candidateEvent = null;
for (final Event event : unsortedEvents)
if (event.getTime().isAfter(timestamp) && (candidateEvent == null || event.getTime().isBefore(candidateEvent.getTime())))
candidateEvent = event;
return candidateEvent;
}
public static Event getNextEventUsingStreams(final List<Event> unsortedEvents, final LocalTime timestamp) {
return unsortedEvents.stream().filter(t -> t.getTime().isAfter(timestamp)).sorted().findFirst().orElse(null);
}
public static Event getNextEventUsingTreeMap(final List<Event> unsortedEvents, final LocalTime timestamp) {
final TreeMap<LocalTime, Event> timeEventMap = new TreeMap<>();
for (final Event event : unsortedEvents)
timeEventMap.put(event.getTime(), event);
final LocalTime key = timeEventMap.ceilingKey(timestamp);
return key != null ? timeEventMap.get(key) : null;
}
It should work just as well with other time classes like java.util.Date instead of java.time.LocalTime as long as they are implements Comparable or you can provide a Comparator.
In case you mess around with this code, you might want to test it. Here's the code relevant to testing which I used for this:
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
public class Event implements Comparable<Event> {
String title;
LocalTime time;
public Event(final String title, final LocalTime time) {
this.title = title;
this.time = time;
}
public Event(final String descriptor) {
this(descriptor.substring(6), LocalTime.parse(descriptor.substring(0, 5)));
}
public String getTitle() { return title; }
public LocalTime getTime() { return time; }
#Override
public int compareTo(final Event o) { return time.compareTo(o.time); }
public static List<Event> createEventList(final String... descriptors) {
final List<Event> events = new ArrayList<>();
for (final String descriptor : descriptors)
events.add(new Event(descriptor));
return events;
}
}
And the Unit Test:
import org.junit.Test;
import java.util.List;
import static java.time.LocalTime.of;
import static java.util.Collections.emptyList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static stackexchange.stackoverflow.q27350515.Event.createEventList;
import static stackexchange.stackoverflow.q27350515.FindNextDate.getNextEvent;
public class FindNextDateTest {
#Test public void givenEmptyList_whenFindingNext_thenReturnsNull() {
assertNull(getNextEvent(emptyList(), of(6, 0)));
}
#Test public void givenOneElementListWithSmallerElement_whenFindingNext_thenReturnsNull() {
final List<Event> events = createEventList("10:15 Breakfast");
assertNull(getNextEvent(events, of(11, 0)));
}
#Test public void givenOneElementListWithLargerElement_whenFindingNext_thenReturnsElement() {
final List<Event> events = createEventList("12:15 Lunch");
assertEquals(events.get(0), getNextEvent(events, of(11, 0)));
}
#Test public void givenBigList_whenFindingNext_thenReturnsElement() {
final List<Event> events = createEventList("10:15 Breakfast", "12:15 Lunch", "08:00 Morning walk", "11:15 Power nap", "14:00 Power nap", "20:00 Dinner");
assertEquals(events.get(3), getNextEvent(events, of(11, 0)));
}
}

Joda-Money persistence via hibernate

There is a library for JodaTime that provides Hibernate persistence. Recently I started looking at Joda-Money and started to see how that can be persisted using hibernate and I do not see any library.
Any suggestions?
Since the link to the example in Sudarshan's answer is broken, here is an implementation of a simple custom user type for org.joda.money.BigMoney, that persists money objects in two columns amount and currency) and an example of how to use it. It works the same for org.joda.money.Money.
package test;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Currency;
import org.apache.commons.lang.ObjectUtils;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
import org.hibernate.usertype.CompositeUserType;
import org.joda.money.BigMoney;
import org.joda.money.CurrencyUnit;
public class MoneyUserType implements CompositeUserType
{
private static final String[] PROPERTY_NAMES = {"amount", "currencyUnit"};
private static final Type[] PROPERTY_TYPES = {StandardBasicTypes.BIG_DECIMAL, StandardBasicTypes.CURRENCY};
public MoneyUserType()
{
super();
}
public Object assemble(final Serializable cached, final SessionImplementor session, final Object owner)
throws HibernateException
{
return cached;
}
public Serializable disassemble(final Object value, final SessionImplementor session) throws HibernateException
{
return (Serializable) value;
}
public String[] getPropertyNames()
{
return PROPERTY_NAMES.clone();
}
public Type[] getPropertyTypes()
{
return PROPERTY_TYPES.clone();
}
public Object getPropertyValue(final Object component, final int property) throws HibernateException
{
BigMoney money = (BigMoney) component;
return (property == 0) ? money.getAmount() : money.getCurrencyUnit().toCurrency();
}
public Object nullSafeGet(final ResultSet rs, final String[] names, final SessionImplementor session,
final Object owner) throws HibernateException, SQLException
{
BigDecimal amount = StandardBasicTypes.BIG_DECIMAL.nullSafeGet(rs, names[0], session);
Currency currency = StandardBasicTypes.CURRENCY.nullSafeGet(rs, names[1], session);
return BigMoney.of(CurrencyUnit.of(currency), amount);
}
public void nullSafeSet(final PreparedStatement st, final Object value, final int index,
final SessionImplementor session) throws HibernateException, SQLException
{
BigMoney money = (BigMoney) value;
BigDecimal amount = (money == null) ? null : money.getAmount();
Currency currency = (money == null) ? null : money.getCurrencyUnit().toCurrency();
StandardBasicTypes.BIG_DECIMAL.nullSafeSet(st, amount, index, session);
StandardBasicTypes.CURRENCY.nullSafeSet(st, currency, index + 1, session);
}
public Object replace(final Object original, final Object target, final SessionImplementor session,
final Object owner) throws HibernateException
{
return deepCopy(original);
}
public void setPropertyValue(final Object component, final int property, final Object value)
throws HibernateException
{
throw new HibernateException("Money is immutable.");
}
public Object deepCopy(final Object value) throws HibernateException
{
return (value != null) ? BigMoney.of(((BigMoney) value).getCurrencyUnit(),
((BigMoney) value).getAmount()) : null;
}
public boolean equals(final Object x, final Object y) throws HibernateException
{
return ObjectUtils.equals(x, y);
}
public int hashCode(final Object x) throws HibernateException
{
return ObjectUtils.hashCode(x);
}
public boolean isMutable()
{
return false;
}
public Class<?> returnedClass()
{
return BigMoney.class;
}
}
Usage:
#Type(type = "test.MoneyUserType")
#Columns(columns = {#Column(name = "AMOUNT"), #Column(name = "CURRENCY")})
private BigMoney money;
The User Type project includes Joda Money support.
The User Type project provides support for joda-money 0.6 since version 3.0.0. Please note however that this requires Hibernate 4. Also the current joda-money version is 0.8
If you want to use it with Hibernate 3 use the example in Sudarshan anwser (it's bugged at the time of writing).
Okay I took your advice and cooked up a custom type for Money as defined in the Joda Library, as a reference people can look it up here,usage here and test for the custom type here
Based on http://jadira.sourceforge.net
Money Types typically consist of a currency and amount. Jadira makes it possible to store only the amount to the database with the currency configured using a parameter. For example:
#Column
#Type(type = "org.jadira.usertype.moneyandcurrency.joda.PersistentMoneyAmount",
parameters = {#org.hibernate.annotations.Parameter(name = "currencyCode", value = "USD")})
private Money money;
Alternatively, with other types two columns to hold the amount an currency:
#Columns(columns = { #Column(name = "MY_CURRENCY"), #Column(name = "MY_AMOUNT") })
#Type(type = "org.jadira.usertype.moneyandcurrency.joda.PersistentMoneyAmountAndCurrency")
private Money money;
Joda-Money's very new, so it's no surprise that noone's provided a Hibernate mapping for it yet.
However, writing custom Hibernate type adapters is pretty straightforward. If you look at the source for the JodaTime adapters, you'll see they're really simple. See the docs for how to write your own.

Categories

Resources