My problem is to persist two classes that have a 1:n relationship:
public class DayRecord {
private Long id;
private List<TimeRecord> timeRecordsToday = new ArrayList<TimeRecord>(4);
...
}
public class TimeRecord {
private Long id;
...
}
So, in code, DayRecord knows TimeRecord.
create table DAY_RECORDS (
id int primary key,
);
create table TIME_RECORDS (
id int primary key,
day_record_id int not null,
foreign key (day_record_id) references DAY_RECORDS (id)
);
In database, TimeRecord knows DayRecord.
Can I save a DayRecord with all its TimeRecords in one step?
In Hibernate, I can set an inverse mapping and just save a DayRecord and all its TimeRecords will get saved, too. With MyBatis, I tried to save the classes independently from each other:
<mapper
namespace="de.stevenschwenke.java.javafx.xyz.DayRecordMapper">
<insert id="insertDayRecord"
parameterType="de.stevenschwenke.java.javafx.xyz.DayRecord">
insert into DAY_RECORDS (id) values (NEXT VALUE FOR DAY_RECORDS_SEQ);
</insert>
</mapper>
<mapper
namespace="de.stevenschwenke.java.javafx.xyz.TimeRecordMapper">
<insert id="insertTimeRecord"
parameterType="de.stevenschwenke.java.javafx.xyz.TimeRecord">
insert into TIME_RECORDS (id) values (NEXT VALUE FOR TIME_RECORDS_SEQ);
</insert>
</mapper>
But how can I save the DayRecord-ID inTimeRecord?
Ideas:
Give TimeRecord an attribute dayRecordId. This way, a cyclic dependency would be created. However, the mapping would take care of the dpenedency while saving.
In one transaction, save the DayRecord first, get its ID, set it in TimeRecords and save this object.
use a nested select-statement within insert like in the documentation
What is the best way to save both objects? Thanks for your help!
As jdevelop already mentioned, MyBatis is just a SQL wrapper. Because SQL doesn't offer a way to insert two objects that have a relationship, MyBatis can't do that either.
So here's my workaround: As I mentioned, I don't want to add a circular dependency by letting TimeRecord know about DayRecord. So I created a wrapper class just for inserting TimeRecords:
public class TimeRecordInsertWrapper {
public Long id;
public int hours;
public long dayRecordId;
[constructor/getter/setter omited but there with public access modifier]
}
First, I store the DayRecord and get it's ID. Then I create the wrapper object and store the TimeRecords:
public long insertDayRecord(DayRecord newRecord) {
SqlSession session = sqlSessionFactory.openSession();
try {
session.insert(
"de.stevenschwenke.java.javafx.xyz.DayRecordMapper.insertDayRecord",
newRecord);
for (TimeRecord tr : newRecord.getTimeRecordsToday()) {
TimeRecordInsertWrapper wrapper = new TimeRecordInsertWrapper(tr.getHours(), newRecord.getId());
session.insert("de.stevenschwenke.java.javafx.xyz.TimeRecordMapper.insertTimeRecord",
wrapper);
}
return newRecord.getId();
} finally {
session.commit();
session.close();
}
}
This way, I can use my nice one-way object model AND have the "right" mapping in the database.
Mybatis is just SQL mapping framework, it allows you to abstract SQL code from Java code and that's it, more or less. They are pretending to look like Hibernate with recent versions, but this leads to weird constructions in XML.
I would suggest to store the DayRecord and get it's it from selectKey, then use that ID in subsequent calls to the mapper. This is what actually happens inside the mapper, but complex XML implies complex FSM to built inside. So keep it simple and you're safe with myBatis, or use Hibernate.
What is even better, you can define custom DAO interfaces for the tasks, and then you can have some sort of Service layer with #Transactional attribute set. This requires mybatis-guice, but it works really great and you don't need to deal with transactions in your code (they are declarative).
Related
How can I avoid unnecessary queries to the DB?
I have LoadEntity with two nested entity - CarrierEntity and DriverEntity. Java class:
#Entity
public class LoadEntity {
...
#ManyToOne
#JoinColumn(name="carrier_id", nullable=false)
private CarrierEntity carrierEntity;
#ManyToOne
#JoinColumn(name="driver_id", nullable=false)
private DriverEntity driverEntity;
}
But API send me carrierId and driverId. I make it:
DriverEntity driverEntity = driverService.getDriverEntityById(request.getDriverId());
loadEntity.setDriverEntity(driverEntity);
loadRepository.save(loadEntity);
How can I write only driverId with JPA?
With Spring Data JPA you can always fall back on plain SQL.
Of course, this will side step all the great/annoying logic JPA gives you.
This means you won't get any events and the entities in memory might be out of sync with the database.
For this reason you might also increase the version column, if you are using optimistic locking.
That said you could update a sing field like this:
interface LoadRepository extends CrudRepository<LoadEntity, Long> {
#Query(query="update load_entity set driver_id = :driverId where carrier_id=:carrier_id", nativeQuery=true)
#Modifying
void updateDriverId(Long carrierId, Long driverId);
}
If you just want to avoid the loading of the DriverEntity you may also use JpaRepository.getById
When using OrientDB Object API in schemaless mode, is it possible to have a field in a POJO marked as unique?
/**
The entity
*/
public class FooEntity {
#Column(unique=true)
private String bar;
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
And this is how the entity is registered, somewhere in another location in the code:
oObjectDatabase.getEntityManager().registerEntityClasses(FooEntity.class.getName());
OrientDB mechanism to map entities does not take into account the JPA annotations, but how could the same effect be achieved?
Would it be the best solution to add the constraint programmatically to the schema after registering the entity?
To add some constraint like unique (that is done through an unique index) you need to define the property in the schema, this means that you cannot have a unique constraint and be schema-less.
And yes for the OrientDB object 2.2.x and before the best way to define unique property is doing some programmatically or scripting schema definition after you registered the entity.
Bye
Is it possible to use auto generated id in Spring Data Gemfire?
for example, if I have a class called MyGemfire
#region("myregion")
class MyGemfire{
#Id
#generatedValue????// if it is not possible what method I have to use to generate id in auto increment fashion?
Long id;
String name;
...
}
From a quick look at SimpleGemfireRepository it doesn't look like the repository is generating an ID:
#Override
public <U extends T> U save(U entity) {
ID id = entityInformation.getId(entity).orElseThrow(
() -> newIllegalArgumentException("ID for entity [%s] is required", entity));
template.put(id, entity);
return entity;
}
Also, this question and its answer suggest there is no ID generation in Gemfire itself.
So what you should do is to create your ID yourself. For example, it should be possible to have two constructors one taking an ID and the othe not taking an ID but generating it. A UUID would be the obvious choice. If you are bound to Long values, you probably have to roll your own algorithm.
To make it obvious to Spring Data which constructor to use when loading instances, you can use the #PersistenceConstructor annotation.
I have two classes
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="PERSONTYPE")
#DiscriminatorValue(value="PERSON")
public class Parent {
.......
}
#Entity
#DiscriminatorValue(value="CHILD")
public class Child extends Parent{
.......
}
The scenario I have:
create a person -- then the PERSONTYPE = 'PERSON'
go the Person page and update it to be 'CHILD' by checking a check box 'Is Child' then after save the Person must be saved to be with type 'CHILD'.
Then how can I change the entity type from 'PERSON' to 'CHILD'?
Here are a couple possibilities:
It seems the obvious thing to do would be to setIsChild(true) on the Parent object and commit it, however I'm not sure how JPA will react to this since you are now committing a Parent and the result is a Child. Not sure this is possible. It is definitely worth a try.
Another option would be to write JPA update statement (circumventing Child and Parent objects) to update the is_child column in the database. Then when you subsequently query this record you will get a Child back not a Parent.
Lastly, you could create a child object with all values of the parent object, then delete the parent, and create the child. This will work, however aside from the extra processing required for delete / create, instead of a simple update, the id of the child may change (it will change if you are using auto generated ids). IMO, this is not a good solution, but it will work.
i assume you have unique properties for Child Object and thats why you want to use inheritance, otherwize just as #ZB Ziet commented you should just use child flag
Solution 1
i see you are using single table inheritance strategy, thus you have to modify the descriminator field manulay (by using SQL queries) and set appropirate fields on child table.
UPDATE Parent SET PERSONTYPE='CHILD' WHERE id = 1
practicaly you use native queries like this
enityManager.createNativeQuery(“UPDATE PERSON SET PERSONTYPE = ?, ”
“ VERSION = VERSION + 1 WHERE ID = ?”)
.setParameter(1, 'CHILD')
.setParameter(2,personID)
.executeUpdate();
then you can use entitymanager to get the child and set properties
entityManager.find(Child.class,1).childProp=xxxx
** Solution 2 **
IMO, The best thing to do here is instead of using single table strategy you should use joined table strategy
in joined table strategy new table entireis is created for each child having thier id as foreign key to parent entity.so changing the id will also set its parent
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Person {
...
it would have been cool if you can just create new child set the id same as parent and save. Child c = new Child(); c.setId(parent.getId()); entityManager.merge(c) . but hibernate tries to re-create a parent object resulting id confilict.
so the solution is to write native query
em.createNativeQuery("INSERT into child (id) VALUES (1)").executeUpdate(); //1 being the parent id
more reference on inheritance
Use EntityMaster
The Idea is to create an Entity just for changing the discriminator.
At least this Entity need to have the id and the discriminator.
#Entity
public class ParentMaster {
public static final PERSON="PERSON";
public static final CHILD="CHILD";
private Long id;
private String persontype;
//getter setter
}
Now load the ParentMaster by id and pm.setPersontype(ParentMaster.CHILD);.
I currently have an object like this (simplified):
public class Image {
public int Id { get; set; }
public int ExternalId { get; set; }
}
Now let's say I have this method (mostly pseudo-code):
public void GetImage(int externalId) {
var existingImage = db.Images.FirstOrDefault(i => i.ExternalId == externalId);
if (existingImage != null) {
return existingImage;
}
var newImage = new Image() { ExternalId = externalId };
db.Images.Attach(newImage);
db.SaveChanges();
return newImage;
}
Because ExternalId isn't a key, the change tracker won't care if I have "duplicate" images in the tracker.
So now, let's say this method gets called twice, at the same time via AJAX and Web API (my current scenario). It's async, so there are two threads calling this method now.
If the time between calls is short enough (in my case it is), two rows will be added to the database with the same external ID because neither existing check will return a row. I've greatly simplified this example, since in my real one, there's a timing issue as I fetch the "image" from a service.
How can I prevent this? I need the image to be returned regardless if it's new or updated. I've added a Unique Constraint in the database, so I get an exception, but then on the client, the call fails whereas it should use the existing image instead of throwing an exception.
If I understand EF correctly, I could handle this by making ExternalId a primary key and then use concurrency to handle this, right? Is there any way to avoid changing my current model or is this the only option?
If you already have property defining uniqueness of your entity (ExternalId) you should use it as a key instead of creating another dummy key which does not specify a real uniqueness of your entity. If you don't use ExternalId as a key you must put unique constraint on that column in the database and handle exception in your code to load existing Image from the database.