I'm hoping someone can point out where I'm going wrong with my mapping for the situatio described below. It's driving me nuts!
I'm trying to map a collection of elements (i.e. existance & lifecycle completely dependent on the parent) which is mapped using a date (joda immutable object) and ordered by the date too. The relational model I'm trying to implement is shown below along with the marked up classes I'm using to do so. When I try to start the application with <property name="hibernate.hbm2ddl.auto" value="create"/> in persistence.xml I get the following exception:
Could not find a setter for property _instrument in class net.mipper.tracker.model.trade.Price
Why is it trying to find a setter? I've annotated the fields directly (I want the object to be immutable) so why is it trying to use a setter? Secondly, is this annotation markup correct for the situation I'm trying to model?
Regards,
mipper
Code:
-------------- ----------------------
| Instrument | | Price |
-------------- ----------------------
| Id (PK) |---<| Instrument_id (PK) |
| Ticker | | Date (PK) |
| Name | | Open_Price |
| Sector | | Close_Price |
-------------- | High_Price |
| Low_Price |
| Volume |
----------------------
@Entity(name="INSTRUMENT")
public class Instrument
{
public Instrument ( final String name,
final String ticker,
final String googleTicker,
final String sector )
{
super ();
_name = name;
_ticker = ticker;
_sector = sector;
}
public Long getId ()
{
return _id;
}
public String getName ()
{
return _name;
}
public String getTicker ()
{
return _ticker;
}
public Collection<Price> getPriceHistory ()
{
return Collections.unmodifiableCollection ( _priceHistory.values () );
}
public void addPrice ( final Price p )
{
if ( null == p )
{
throw new IllegalArgumentException ( "Attempt to add null Price." );
}
if ( !p.getInstrument ().equals ( this ) )
{
throw new IllegalArgumentException ( String.format ( "Attempt to add a price for a different instrument: %1s, %2s",
_ticker,
p.getInstrument ().getTicker () ) );
}
_priceHistory.put ( p.getDate (), p );
}
public void addPrices ( final Collection<Price> prices )
{
for ( final Price p: prices )
{
addPrice ( p );
}
}
public Price getPrice ( final LocalDate date )
{
return _priceHistory.get ( date );
}
@Override
public boolean equals ( final Object obj )
{
if ( obj == this )
{
return true;
}
if ( !( obj instanceof Instrument ) )
{
return false;
}
return _ticker.equals ( ( ( Instrument ) obj ).getTicker () );
}
@Override
public int hashCode ()
{
return _ticker.hashCode ();
}
protected Instrument ()
{
super ();
}
@Id @GeneratedValue
@Column(name = "ID")
private Long _id;
@Column(name = "NAME")
private String _name;
@Column(name = "TICKER", nullable = false, unique=true)
@Basic(optional = false)
private String _ticker;
@Column(name = "SECTOR")
private String _sector;
@org.hibernate.annotations.CollectionOfElements
@JoinTable(
name = "PRICE",
joinColumns = @JoinColumn(name = "INSTRUMENT_ID")
)
@org.hibernate.annotations.MapKey(
columns = @Column(name="PRICE_DATE"),
type=@org.hibernate.annotations.Type(type="org.joda.time.contrib.hibernate.PersistentLocalDate"))
@org.hibernate.annotations.OrderBy(clause = "PRICE_DATE desc")
private final Map<LocalDate, Price> _priceHistory = new HashMap<LocalDate, Price> ();
}
@Embeddable
public class Price
{
/**
* Constructor.
*
* @param instrument
* @param date
* @param openPrice
* @param lowPrice
* @param highPrice
* @param closePrice
* @param volume
*/
public Price ( final Instrument instrument,
final LocalDate date,
final BigDecimal openPrice,
final BigDecimal lowPrice,
final BigDecimal highPrice,
final BigDecimal closePrice,
final Long volume )
{
super ();
_instrument = instrument;
_priceDate = date;
_openPrice = openPrice;
_lowPrice = lowPrice;
_highPrice = highPrice;
_closePrice = closePrice;
_volume = volume;
}
/**
* @return The instrument
*/
public Instrument getInstrument ()
{
return _instrument;
}
/**
* @return The date
*/
public LocalDate getDate ()
{
return _priceDate;
}
/**
* @return The openPrice
*/
public BigDecimal getOpenPrice ()
{
return _openPrice;
}
/**
* @return The lowPrice
*/
public BigDecimal getLowPrice ()
{
return _lowPrice;
}
/**
* @return The highPrice
*/
public BigDecimal getHighPrice ()
{
return _highPrice;
}
/**
* @return The closePrice
*/
public BigDecimal getClosePrice ()
{
return _closePrice;
}
/**
* @return The volume
*/
public Long getVolume ()
{
return _volume;
}
@Override
public boolean equals ( final Object obj )
{
if ( obj == this )
{
return true;
}
if ( ! ( obj instanceof Price ) )
{
return false;
}
final Price rhs = ( Price ) obj;
return new EqualsBuilder ().appendSuper ( super.equals ( obj ) )
.append ( _instrument, rhs._instrument )
.append ( _priceDate, rhs._priceDate )
.isEquals ();
}
@Override
public int hashCode ()
{
return new HashCodeBuilder ( 79, 62617 ).append ( _instrument )
.append ( _priceDate )
.toHashCode ();
}
/**
* Default Constructor.
*/
protected Price ()
{
super ();
}
@Id
@Parent
@Column(name="INSTRUMENT_ID", nullable=false)
private Instrument _instrument;
@Id
@Column(name="PRICE_DATE", nullable=false)
@Type(type="org.joda.time.contrib.hibernate.PersistentLocalDate")
private LocalDate _priceDate;
@Column(name="OPEN_PRICE")
private BigDecimal _openPrice;
@Column(name="LOW_PRICE")
private BigDecimal _lowPrice;
@Column(name="HIGH_PRICE")
private BigDecimal _highPrice;
@Column(name="CLOSE_PRICE", nullable=false)
private BigDecimal _closePrice;
@Column(name="VOLUME")
private Long _volume;
}