Using MongoDB "_id" field as primary key within DataNucleus - mongodb

I am new to MongoDB and JDO after mostly doing development with Hibernate in the past. I am trying to persist a simple object and leverage the generated "_id" from MongoDB as the primary key for the persisted object. Unfortunately, it looks like DataNucleus is generating an "IDENTITY" field as well as Mongo generating an "_id" field in the persisted document. So, every object is persisted with two unique identifiers. How can I enforce DataNucleus to simply use the generated Mongo ObjectId? My persistent class is below.
#PersistentCapable(identityType=IdentityType.DATASTORE)
public class HistoricalPrice {
private String ticker;
private Date day;
private double open;
private double close;
private double high;
private double low;
private long volume;
public HistoricalPrice(String ticker, Date day, double open, double close, double high, double low, long volume) {
super();
this.ticker = ticker;
this.day = day;
this.open = open;
this.close = close;
this.high = high;
this.low = low;
this.volume = volume;
}

Define the datastore identity "strategy" to be IDENTITY (as opposed to the default of NATIVE).
#DatastoreIdentity(strategy=IdGeneratorStrategy.IDENTITY)
i.e consistent with what would be needed on an RDBMS to use some inbuilt mechanism.

Related

How to make a data class in Kotlin immutable with java Date object in it?

The java.util.Date itself is a mutable object. As such even if Kotlin data class (with date field declared val) prevents me from changing the reference I can modify the date object itself to change its value.
Ways I could come up with:
Use normal class, override getter and setter. In each use the clone method to make a copy of given date.
#Column(name = "db_date")
private var dbDate: Date? = null
get() = dbDate?.clone() as Date
set(date) {
field = date?.clone() as Date
}
Also I can't use the copy method of data class since these classes are hibernate entities. So I need to modify them via setters.
The reason I want to use data classes for my entities is because these implement equals and hashcode by default. We had been using lombok in java for this and now convincing team to create these methods is tough. Even if generation happens by IDE its still going to be checked into source control.
So is there any way I could do custom setters on data class logic. Or any way I can generate equals and hashcode for normal classes without checking them in source control?
Edit: In comments it was pointed out to use java.time.Instant which is Immutable. The issue I am faced with is that this is a Hibernate Entity Class and we are using hibernate 3.6. Instant support came in hibernate 5.2 so we are way behind and migration of hibernate will be a heavy task. What I did notice is that kotlin data classes do allow setters and getters just in a different way. Code below:
#Entity
#Table(name = "my_table")
data class MyTable(
#Id
#Column(name = "id")
var id: Long? = null,
#Column(name = "my_date")
private var date: Date? = null,
) {
fun getDate():Date = gradedDate?.clone() as Date
fun setDate(date: Date?) {
this.date = date?.clone() as Date
}
}
You can do it with some hack:
#Entity
#Table(name = "my_table")
data class DateWrapper(
#Id
#Column(name = "id")
val id: Long?,
#Column(name = "my_date")
private var _date: Date?
) {
init {
_date = _date?.clone() as Date
}
val date = _date
}
Try using java.sql.Date instead

Get only selected data from JPA query

I cound't find any nice solution to get only selected data from the domain?
E.g I have class:
#Entity
public class Reservation {
// private Integer RESERVATION_ID;
// private Integer id;
private long code;
private Date date;
private Client reservationClient;
private WashType reservationWashType;
private Vehicle reservationVehicle;
private Wash reservationWash;
private Worker reservationWorkerPesel;
private Review reservationReview;
private ReservationReminder reservationReminder;
}
And have simple query repository
public interface ReservationRepository extends JpaRepository<Reservation, Long> {
Reservation findByCode(long code);
}
I'd like to take from that query the Reservation object but without data's from class like Review, Worker.
So it means my result should looks like:
a whole object of Reservation which includes:
code,date,Client reservationClient,WashType reservationWashType,Vehicle reservationVehicle,Wash reservationWash, ReservationReminder reservationReminder
Is it possible to exclude it in nice way? Or if not how can I manage it?
Yes, you can easily do that so long as Review and Worker are marked to be lazily loaded.
What I mean is:
#ManyToOne(fetch = FetchType.LAZY)
private Review review;
Hibernate won't attempt to load the Review association until you call #getReview().
For situations then where you want your Reservation with the Review, you would then just need to specify at query-time that you want the relationship join-fetched.
#Query("SELECT r FROM Reservation r JOIN FETCH r.review WHERE r.code = :code")
List<Reservation> findByCode(Long code);
Remember, if Review cannot be null, make sure that #ManyToOne has the optional=false attribute so that when the join gets generated, it uses an inner join rather than an outer join to avoid performance overhead.

Spring data elastic search findAll with OrderBy

I am using spring data's elastic search module, but I am having troubles building a query. It is a very easy query though.
My document looks as follows:
#Document(indexName = "triber-sensor", type = "event")
public class EventDocument implements Event {
#Id
private String id;
#Field(type = FieldType.String)
private EventMode eventMode;
#Field(type = FieldType.String)
private EventSubject eventSubject;
#Field(type = FieldType.String)
private String eventId;
#Field(type = FieldType.Date)
private Date creationDate;
}
And the spring data repository looks like:
public interface EventJpaRepository extends ElasticsearchRepository<EventDocument, String> {
List<EventDocument> findAllOrderByCreationDateDesc(Pageable pageable);
}
So I am trying to get all events ordered by creationDate with the newest event first. However when I run the code I get an exception (also in STS):
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property desc found for type Date! Traversed path: EventDocument.creationDate.
So it seems that it is not picking up the 'OrderBy' part? However a query with a findBy clause (eg findByCreationDateOrderByCreationDateDesc) seems to be okay. Also a findAll without ordering works.
Does this mean that the elastic search module of spring data doesn't allow findAll with ordering?
Try adding By to method name:
findAllByOrderByCreationDateDesc

How to cache referenced entities in Morphia?

I'm using Morphia for MongoDB with Stripes Framework.
Let us assume I have two entities, Car (which describes a specific car, say some particular 1984 Honda Accord) and CarType (which specifies all Honda Accords of that kind):
The most natural way to model this seems:
#Entity
class Car {
#Id private String id; // VIN
private Date purchaseDate;
private Color color;
#Reference private CarType type;
// ..
}
#Entity
class CarType {
#Id private String id;
private String manufacturerId;
private float engineDisplacement;
// ..
}
This works, but is inefficient, as CarType is looked up from DB every time a Car is loaded. I would like to cache car types in memory, as they change rarely. Persistence frameworks like GORM and Hibernate would allow that out of the box, but I'm not sure how to do it under Morphia (there is a feature request raised for that).
I'd like to keep the reference to CarType, as just storing a String carTypeId would complicate the views and everything else too much.
So I thought I could do something like this:
#Entity
class Car {
#Id private String id; // VIN
private Date purchaseDate;
private Color color;
private String typeId;
#Transient private CarType type;
#Transient private CarService service = new CarServiceImpl();
public void setTypeId() {
this.typeId = typeId;
updateTypeReference();
}
#PostLoad void postLoad() {
updateTypeReference();
}
private void updateTypeReference() {
type = service.findTypeById(typeId);
}
// ..
}
class CarServiceImpl implements CarService {
#CacheResult CarType findCarTypeId(String typeId) {
datastore.get(CarType.class, typeId);
}
// ..
}
Which works and does what I want, but:
Does seem like a hack
I'd to inject the service instead using Guice, but cannot figure out how, although I have overall dependency injection working in Stripes ActionBeans.
So I'd like to either:
Learn how to inject (preferably, Guice) services into Morphia entities
or
Learn how to otherwise properly do caching for referenced entities in Morphia
or
If all else fails, switch to some other MongoDB POJO mapping approach which supports caching. But I really like Morphia so I'd rather not.
Another common approach would be to embed the CarType in each Car. That way you would only have to fetch a single entity.
Trade-offs:
You'll need an update logic for all duplicated CarTypes. Since you said that they hardly change, this should be fine performance-wise.
Duplicated data requires additional disk-space and the working set in RAM gets bigger as well.
You'll need to evaluate how this works out for your data, but data duplication to make reads faster is quite a common approach...
Since I didn't think of a better solution I am doing a #PostLoad event handler which gets the datastore class from a static variable, and can then look up the Referenced entity.
That seems like a hack and requires the datastore service to be thread-safe, but it works for me.

Default Fetch group in MongoDB - dataneucleus

I use datanucleus and MongoDB for storing my objects. I detected problems with the lazy loading.
One on My class is :
public class Member implements Serializable{
private static final long serialVersionUID = 1L;
#PrimaryKey
#Persistent(defaultFetchGroup = "true", valueStrategy = IdGeneratorStrategy.IDENTITY)
private String key;
private String username;
#Persistent(defaultFetchGroup="true",dependent="true")
private Parameter param = null;
}
And the code for retrieving this object is :
Transaction tx = pm.currentTransaction();
tx.begin();
Member member = pm.getObjectById(Member.class,"MyID");
tx.commit();
//if I check here, the field "param" is null.
When I check, the field "param" is null. However, I set the meta data to load by default the param. Maybe the driver MongoDB-JDO doesn't support the metadata "defaultFetchGroup".
Could you tell me what happens?
Thanks a lot.
What the situation is "here" (outside the transaction) depends totally on the object lifecycle and what persistence options you have enabled. That link defines it. Likely the object is HOLLOW, so fields have been unloaded and you didnt set "datanucleus.RetainValues"