My questions i about storing one-to-many relationship to database (as Programming a one-to-many relationship)
Lets assume the example: we have parent and children. Each parent record can have many childs.
// Db storage:
// id name
public class Parent {
Set<Childs> mChilds;
private long mId;
private String mName;
// etc
public Parent(String name) {
this.mName = name;
}
public static Parent load(id) {
// query database for all Parent attributes
Parent p = new Parent("somename");
// now its time to load set of child (1)
Set<Child> children = Child.loadChildren(p);
}
}
// Db storage
// id name parent_ref
public class Child {
private Parent mParent;
private String mName;
public Child(String name, Parent parent) {
this.mName = name;
this.mParent = parent;
}
// loads a child with specific Id
public static Child load(long id) {
// query db like
Cursor c = db.query("child_table", new String[] {"id", "name", "parent_ref"}, "id = ?", new String[] {String.valueOf(id)});
// now we have a cursor, we can construct child!
// Oh my God! I call Parent.load, that calls loadChildren(3)
return new Child(c.getColumn("name"), Parent.load(c.getColumn("parent_ref")));
}
public static Set<Child> loadChildren(Parent parent){
// fetch all child ids, that given parent has
List<long> childIds = getAllIds(parent);
// for each id load child (2)
Child c = Child.load(childId);
}
}
As you can see, I'd like to load parent by given Id. Parent's load function call child's loadlChildren (call (1)), which calls Child.load(call (2)), which calls Parent.load (3) because it needs to make a reference to parent!
So there are two major questions as Im new to java dev.
1) Are there any workarounds about this problems? what Im doing wrong?
2) My call to loadChildren will create n Parents (n references to n objects) instead of creating references to one object. What should I do?
Why don't you implement some kind of cache system (maybe with a Java HashMap) where you can put a parent and its children and instead of loading them anyway, load them only if they aren't already in the cache? And get a reference to them if they are alredy in the cache.
I'd also have a look at the: Flyweight pattern
Or another solution I can think is to implement another method in the Parent class: somethink like "loadOnlyParent()" which loads only the Parent (if not already loaded) without the children. And implement a lazy loading of the children (if not already loaded) only when necessary.
Related
This question is somehow related to my last question, because it is the same project but now I am trying to go one more step forward.
So, in my previous question I only had one table; this time I have two tables: the new second table is supposed to contain related attributes for the rows of the first table, in a OneToMany relationship. So, I store a ForeignKey in the second table that would store the Row ID of the first table's related row (obviously).
The problem is this: the intention is creating both registers (parent and child) at the same time, using the same form, and ParentTable uses AUTO_INCREMENT for his PrimaryKey (AKA ID).
Due to how RoomDb works, I do the creation using a POJO: but after insertion, this POJO won't give me the auto-generated ID as far as I know... so, the only workaround I am able to imagine is, when submitting the form, first make the INSERT for the parent, then using one of the form's fields that created the parent to make some kind of "SELECT * FROM parent_table WHERE field1 LIKE :field1", retrieving the ID, and then use that ID to create the child table's POJO and perform the next INSERT operation. However I feel something's not right about this approach, the last time I implemented something similar this way I ended up with a lot of Custom Listeners and a callback hell (I still have nightmares about that).
About the Custom Listeners thing, it is the solution I ended up choosing for a different problem for a different project (more details about it in this old question). Taking a look to that old question might help adding some context about how misguided I am in MVVM's architecture. However, please notice the current question has nothing to do with WebServices, because the Database is purely local in the phone's app, no external sources.
However, I am wondering: isn't this overkill (I mean the INSERT parent -> SELECT parentID -> INSERT child thing)? Is it inevitable having to do it this way, or there is rather a more clean way to do so?
The "create method" in my Repository class looks like this:
public void insertParent(Parent parent) {
new InsertParentAsyncTask(parent_dao).execute(parent);
}
private static class InsertParentAsyncTask extends AsyncTask<Parent, Void, Void> {
private final ParentDao parent_dao;
private InsertParentAsyncTask(ParentDao parent_dao) {
this.parent_dao = parent_dao;
}
#Override
protected Void doInBackground(Parent... parents) {
parent_dao.insert(parents[0]);
return null;
}
}
Trying to follow Mario's answer, I changed this method in my parent's DAO:
// OLD
#Insert
void insert(Parent parent);
// NEW (yes, I use short type for parent's ID)
#Insert
short insert(Parent parent);
EDIT2:
Now, I am trying to make changes to my Repository's insert AsyncTask, like this:
private static class InsertParentAsyncTask extends AsyncTask<Parent, Void, Short> {
private final ParentDao parent_dao;
private InsertParentAsyncTask(ParentDao parent_dao) {
this.parent_dao = parent_dao;
}
#Override
protected Short doInBackground(Parent... parents) {
short parent_id;
parent_id = parent_dao.insert(parents[0]);
return parent_id;
}
#Override
protected void onPostExecute(Short hanzi_id) {
// TODO ??? What now?
}
}
LONG STORY SHORT
It worked for me this way down here, but this ain't clean code (obviously):
// TODO I am aware that AsyncTask is deprecated
// My Repository class uses this
public void insertParentAndChildren(Parent parent, String[] children_list) {
new InsertParentAndChildrenAsyncTask(parent_dao, children_list).execute(parent);
}
private static class InsertParentAndChildrenAsyncTask extends AsyncTask<Parent, Void, Short> {
private final ParentDao parent_dao;
private String[] children_list;
private InsertParentAndChildrenAsyncTask(ParentDao parent_dao, String[] children_list) {
this.parent_dao = parent_dao;
this.children_list = children_list;
}
#Override
protected Short doInBackground(Parent... parents) {
short parent_id;
Long row_id = parent_dao.insert(parents[0]);
parent_id = parent_dao.getIdForRowId(row_id);
return parent_id;
}
#Override
protected void onPostExecute(Short parent_id) {
// Second "create method" for children
for (int n = 0; n < children_list.length; n++) {
Child child = new Child();
child.setParentId( parent_id );
child.setMeaning( children_list[n] );
// My Repository has this method as well
insertChildStaticMethod(child);
}
}
}
You are on the right track. A clean way would be to wrap it in a function like this:
fun saveParent(parent: Parent): Int {
val rowId = parentDao.insert(parent) // Returns Long rowId
val parentId = parentDao.getIdForRowId(rowId) // SELECT id FROM table_parent WHERE rowid = :rowId
return parentId
}
This function could be part of a repository class to make it even more clean.
Your functions in DAO can return the rowId and Parent.ID like this:
#Insert
fun insert(parent: Parent): Long
#Query("SELECT ID FROM table_parent WHERE rowid = :rowId")
fun getIdForRowId(rowId: Long): short
If you want to get basic functionality working first, you can call the Room database functions on the main thread when you build your database with allowMainThreadQueries():
MyApp.database = Room.databaseBuilder(this, AppDatabase::class.java, "MyDatabase").allowMainThreadQueries().build()
Like this, you can postpone background processing to later. If you have specific questions about that subject, it is better to ask a separate question.
I think you could try SELECT last_insert_rowid() as a query on room (you write it just like that no need to reference any table). This statement returns the rowid of the last insertion into your database. By default rowId is what most sql DBs use as primary keys when you define them as auto incremental integers. so I guess you would define the following method in your DAO
#Query("SELECT last_insert_rowid()")
public abstract int getLastId()
#Insert
void insert(Parent parent)
then you can use it together with your insert statement in a transaction. like so
#Transaction
public int insertAndGetPrimaryKey(Parent parent){
insert(parent);
return getLastId();
}
it is important to use transaction as else the id delivered could be the wrong one if in your app multiple threads could potentially modify the tables at the same time.
btw I would not use short for a primary key. not only is it short XD (only 32k capacity) but the satndard is really to use Integer (4 bn capacity). if these were the 80's id be all for it (those are 2 entire bytes that you are saving after all XD) but now a days memory is cheap and abundant and as i said integer is what DBs work with by default.
I have a parent -> child relationship, with a #ManyToOne / #OneToMany relationship.
I'm processing updates to the parent, in code that goes roughly like this:
Get the parent
Retrieve from (in order - ehCache, db, or create if not found)
Process an update, creating a child on the parent if not found
Save to the db
Store in the cache
When running through, I find the following sequence occurs
First update completes - parent & child both created an cached
Second update - parent retrieved from the cache, new child is added
When the second update completes, the child's id is still null. However, the update did complete successfully. (verified against both hibernate logs and db)
Third update - DataIntegrityViolationException is thrown, as the child from the 2nd update is INSERTed again.
I assume that this must be related to the fact the parent is cached, rather than returned from the database. I'm not sure what the correct process here should be.
Relevant information:
The Parent <--> child back references are defined and annotated correctly.
After the initial INSERT of the parent, I've tried re-fetching the parent from the db, and caching this, to see if it made a difference --- it didn't.
Transactional boundaries must be playing a role here, as this initially didn't fail in my tests that were annotated as #Transactional. (A lesson hard-learnt)
Whats the correct way to handle this - specifically, to avoid having to load the Parent from the db every time, while still having child entities tracked correctly?
Code example shown below.
#Entity // Parent
class Fixture {
#OneToMany(cascade=CascadeType.ALL, mappedBy="fixture", fetch=FetchType.EAGER) #Getter #Setter
#MapKey(name="instrumentPriceId")
private Map<String,Instrument> instruments = Maps.newHashMap();
private Instrument addInstrument(Instrument instrument)
{
instruments.put(instrument.getInstrumentPriceId(), instrument);
instrument.setFixture(this);
log.info("Created instrument {}",instrument.getInstrumentPriceId());
return instrument;
}
/**
* Returns an instrument with the matching instrumentId.
* If the instrument does not exist, it is created, appended to the internal collection,
* and then returned.
*
* This method is guaranteed to always return an instrument.
* This method is thread-safe.
*
* #param instrumentId
* #return
*/
public Instrument getInstrument(String instrumentId)
{
if (!instruments.containsKey(instrumentId))
{
addInstrument(new Instrument(instrumentId));
}
return instruments.get(instrumentId);
}
}
#Entity // Child
public class Instrument {
#Column(unique=true)
#Getter #Setter
private String instrumentPriceId;
#ManyToOne(optional=false)
#Getter #Setter #JsonIgnore
private Fixture fixture;
public Instrument(String instrumentPriceId)
{
this.instrumentPriceId = instrumentPriceId;
}
}
And, the update processor code:
class Processor {
#Autowired
#Qualifier("FixtureCache")
private Ehcache fixtureCache;
#Autowired
private FixtureRepository fixtureRepository;
void update(String fixtureId, String instrumentId) {
Fixture fixture = getFixture(fixtureId);
// Get the instrument, creating it & appending
// to the collection, if it doesn't exist
fixture.getInstrument(instrumentId);
// do some updates...ommitted
fixtureRepository.save(fixture);
fixtureCache.put(new Element(fixtureId, fixture));
}
/**
* Returns a fixture.
* Returns from the cache first, if present
* If not present in the cache, the db is checked.
* Finally, if the fixture does not exist, a new one is
* created and returned
*/
Fixture getFixture(String fixtureId) {
Fixture fixture;
Element element = fixtureCache.get(fixtureId);
if (element != null)
{
fixture = element.getValue();
} else {
fixture = fixtureRepostiory.findOne(fixtureId);
if (fixture == null)
{
fixture = new Fixture(fixtureId);
}
}
return fixture;
}
}
The answer to this was frustratingly simple.
In the update method, I was ignoring the result of the save() operation.
Often, this is fine, if you're not planning on using object again. (which is common, as you save right at the end of your unit of work).
However, as I was continuing to use my 'parent' again, I needed to observe the returned value:
So this:
fixtureRepository.save(fixture);
fixtureCache.put(new Element(fixtureId, fixture));
becomes this:
fixture = fixtureRepository.save(fixture);
fixtureCache.put(new Element(fixtureId, fixture));
I am working on an object cache of CMS objects. I need to maintain a parent/child relationship between a Product and child objects (Options and Attributes). The parent (Product) is illustrated in the first code sample.
It is easy enough to do, but I am looking for a way to make the assignment of the child to the parent, as shown in the 2nd code block, generic.
Since all CMS objects extend CMSContent, I can use ProductID. However, is there a way to make the field (e.g. ProductAttribute) generic so that I can put the algorithm in a method and call the method with a parent and child object to make the attribute assignment?
I know that an ORM framework like Hibernate is appropriate here, but that won't fit since I have a fixed database structure.
public class Product extends CMSContent {
private List<ProductAttribute> productAttributes;
private List<ProductOptions> productOptions;
// getters,setters
}
Algorithm to match them up.
// attach Product Attributes to Product
for (Product p : listP) {
Map<String, Object> parameters = new HashMap<String, Object>();
for (ProductAttribute po : listPA) {
parameters.put("pid", p.getPid());
parameters.put("ref", po.getRid());
int i = jdbcTemplate.queryForInt(sqlAttr, parameters); // select count(*), 1 if matched.
if (i == 1) {
p.getProductAttributes().add(po); // generic field?
}
}
}
Wouldn't this two Methods in Product help
public void add(ProductAttribute item){
productAttributes.add(item);
}
public void add(ProductOption item){
productOption.add(item);
}
so you should be able to just add a ProductAttribute or a ProductOption
Given a Parent class which has many Children, can Hibernate automatically manage the order of said children? From reading documentation and searching, it seems that the #OrderColumn annotation may enable this, but I've not found any examples of how to do it. The closest thing I've found is JPA 2.0 #OrderColumn annotation in Hibernate 3.5, which looks a bit discouraging, given that it looks just like what I want to do.
Here is a rough sketch of what I'm trying to do, minus the annotations, since I'm not sure what they would be:
class Parent {
// I probably need some sort of #OrderColumn annotation here, right?
private List<Child> children = new ArrayList<Child>;
}
class Child {
private Parent parent;
private int order;
}
class SomeBusinessLogic {
public static void createFamily() {
Parent dad = new Parent("Tom");
List<Children> children = dad.getChildren();
children.add(new Child("Alice");
children.add(new Child("Bob");
children.add(new Child("Carl");
session.save(dad);
}
public static void addChild(Parent parent) { // assuming Tom is passed in
List<Children> children = parent.getChildren();
children.add(1, new Child("Zelda");
session.save(parent);
// now, the order should be: Alice, Zelda, Bob, Carl
}
}
If someone can give me enough details to make this toy example work (or tell me that it will not work), I would be most appreciative.
In order to store children in its order you should map it with #IndexedColumn annotation it will create indexed column on your child objects table.
My first project with Hibernate/JPA and Play!, I've got a menu that once working, will support changes (i.e. easily add nodes to the tree). Struggling (in the >5 hours sense) just to get the basic modelling together:
The model:
#Entity
#Table(name = "Node")
public class Node extends Model {
#Column(nullable=false)
public String description;
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="parent")
public List<Node> children = new LinkedList<Node>();
#ManyToOne
#JoinColumn(name="parent", insertable=false, updatable=false)
public Node parent;
public Node(){}
}
The util class:
public class NodeUtil {
public static void addChild(Node root, String description) {
Node child = new Node();
child.description = description;
child.parent = root;
root.children.add(child);
root.save();
}
private static final String MENU_NAME = "MainMenu";
public static Node getMenu() {
return getRoot(MENU_NAME);
}
public static Node getRoot(String name) {
Node root = Node.find("byDescription", name).first();
if (root == null) {
root = new Node();
root.description = name;
root.save();
}
return root;
}
}
The test:
public class MenuTest extends UnitTest {
private static final String TEST_MENU = "testMenu";
#Test
public void testMenu() {
// test build/get
Node root = NodeUtil.getRoot(TEST_MENU);
assertNotNull(root);
// delete all children - maybe from previous tests etc.
for(Node o : root.children)
o.delete();
root.save();
// test add
root = NodeUtil.getRoot(TEST_MENU);
NodeUtil.addChild(root, "child 1");
NodeUtil.addChild(root, "child 2");
NodeUtil.addChild(root, "child 3");
assertEquals(3, root.children.size());
assertEquals("child 3", root.children.get(2).description);
assertEquals(0, root.children.get(1).children.size());
Node node = root.children.get(1);
NodeUtil.addChild(node, "subchild 1");
NodeUtil.addChild(node, "subchild 2");
NodeUtil.addChild(node, "subchild 3");
NodeUtil.addChild(node, "subchild 4");
NodeUtil.addChild(root.children.get(2), "sub subchild");
assertEquals("sub subchild", root
.children.get(1)
.children.get(2)
.children.get(0)
.description);
assertEquals(4, root.children.get(1).children.size());
root.save();
// test delete
root = NodeUtil.getRoot(TEST_MENU); // regrab the root via hibernate, assuming there isnt it isnt cached this will get changes that have been persisted to the db (maybe?)
root.children.get(1).children.get(2).delete();
assertEquals(3, root.children.get(1).children.size());
//root.delete();
}
}
Questions:
What am I doing wrong? (I.e. I just can't get this simple idea to be modelled and to pass the unit test. Like I said, new to Hibernate, and every change I make yields a new Hibernate error variant, which means nothing to me. E.g. this current setup throws "detached entity passed to persist: models.Node")
Initially I had the entire util class as a bunch of static methods in the model class. Firstly, do static methods affect Hibernates modelling? If so, in brief, under what circumstances can I have static methods (and members, come to think of it) that will be "transient" to the object modelling?
Assuming that I keep the util methods in a separate public util class, where is this class conventionally stored in the play framework? At the moment it's in the models package, next to the Node.java.
I'm not familiar with Play Framework, but I can make some point regarding working with Hibernate:
Maintaining consistent state of the objects in memory is your responsibility. Consider the following code:
for(Node o : root.children)
o.delete();
root.save();
You instructed Hibernate to delete children from the database, but the root object in memory still references them. Since the relationship is configured with cascading, Hibernate will try to save them again (I guess it's the reason of "detached entity passed to persist" error). So, keep in-memory state of the object consistent by clearing root.children.
Hibernate heavily relies on the concept of Unit of Work. I'm not sure how Play manages it, but it looks like you should call clearJPASession() in unittests to make sure that exisiting session state wouldn't affect further operations:
root.save();
clearJPASession();
// test delete
root = NodeUtil.getRoot(TEST_MENU);
The way you defined a relationship is supported, but not recommended (see 2.2.5.3.1.1. Bidirectional). Use the following approach instead:
#OneToMany(mappedBy = "parent", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
public List<Node> children = new LinkedList<Node>();
#ManyToOne
#JoinColumn(name="parent")
public Node parent;
Static methods doesn't interfere with Hibernate.