Discriminator column not set in newly persisted entity - jpa-2.0

I have a base class entity that looks like this:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "IDTYPE",
discriminatorType = DiscriminatorType.STRING,
length = 12)
public class ProtoObject implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String idtype;
// more columns
I have a number of sub-classes of this that I build and persist. However I have found that even though idtype column gets set to what I would expect in the data base (checking with a separate DB browser tool) the Java objects idtype property does not get set for those object that the EntityManager has in its cache.
If I do a find of objects through the EntityManager, the most recently persisted objects appear to have idtype blank! If I close the application and re-open it the data appears OK.
This is a JSF 2 application using EclipseLink 2.3.2.v20111125-r1046.
Is this a known problem and can anyone suggest a work-around?

You can update cache after persisting:
this.entityManager.persist(protoObject);
this.entityManager.flush();
this.entityManager.refresh(protoObject);

You didn't set the attribute when you created the object to reflect what you wanted pushed to the database. You need to set the field to keep it consistent in the cache as JPA Entities are treated as regular java objects and won't fix things for you. Or after persist you can flush the changes them refresh the object to have the field populated with what is in the DB

Related

Doctrine swap out table at runtime

Typically when you implement a entity using Doctrine you map it to a table explicitly:
<?php
/**
* #Entity
* #Table(name="message")
*/
class Message
{
//...
}
Or you reply on doctrine to implicitly map your class name to a table...I have several tables which are identical in schema but I do not wish to re-create the class for each time...there fore at runtime (dynamically) I would like to change the table name accordingly.
Where do I start or what would I look into overriding to implement this odd requirement???
Surprisingly (to me), the solution is very simple. All you have to do is to get the ClassMetadata of your entity and change the name of the table it maps to:
/** #var EntityManager $em */
$class = $em->getClassMetadata('Message');
$class->setPrimaryTable(['name' => 'message_23']);
You need to be careful and do not change the table name after you have loaded some entities of type Message and changed them. It's a big chance it will either produce SQL errors on saving (because of table constraints, for example), if you are lucky or it will modify the wrong row (from the new table).
I suggest the following workflow:
set the desired table name;
load some entities;
modify them at will;
save them;
detach them from the entity manager (the method EntityManager::clear() is a quick way to start over);
go back to step 1 (i.e. repeat using another table).
The step #5 (detach the entities from the entity manager) is useful even if you don't change or don't save the entities. It allows the entity manager use less memory and work faster.
This is just one of the many methods you can use to dynamically set/change the mapping. Take a look at the documentation of class ClassMetadata for the rest of them. You can find more inspiration in the documentation page of the PHP mapping.

Cannot make #ManyToOne relationship nullable

I have a many-to-one relationship that I want to be nullable:
#ManyToOne(optional = true)
#JoinColumn(name = "customer_id", nullable = true)
private Customer customer;
Unfortunately, JPA keeps setting the column in my database as NOT NULL. Can anyone explain this? Is there a way to make it work? Note that I use JBoss 7, JPA 2.0 with Hibernate as persistence provider and a PostgreSQL 9.1 database.
EDIT:
I found the cause of my problem. Apparently it is due to the way I defined the primary key in the referenced entity Customer:
#Entity
#Table
public class Customer {
#Id
#GeneratedValue
#Column(columnDefinition="serial")
private int id;
}
It seems that using #Column(columnDefinition="serial") for the primary key automatically sets the foreign keys referencing it to NOT NULL in the database. Is that really the expected behavior when specifying the column type as serial? Is there a workaround for enabling nullable foreign keys in this case?
Thank you in advance.
I found the solution to my problem. The way the primary key is defined in entity Customer is fine, the problem resides in the foreign key declaration. It should be declared like this:
#ManyToOne
#JoinColumn(columnDefinition="integer", name="customer_id")
private Customer customer;
Indeed, if the attribute columnDefinition="integer" is omitted the foreign key will by default be set as the source column: a not-null serial with its own sequence. That is of course not what we want as we just want the to reference the auto-incremented ID, not to create a new one.
Besides, it seems that the attribute name=customer_id is also required as I observed when performing some testing. Otherwise the foreign key column will still be set as the source column. This is a strange behavior in my opinion. Comments or additional information to clarify this are welcome!
Finally, the advantage of this solution is that the ID is generated by the database (not by JPA) and thus we do not have to worry about it when inserting data manually or through scripts which often happens in data migration or maintenance.
I came across this problem but I was able to solve it this way:
#ManyToOne
#JoinColumn(nullable = true)
private Customer customer;
Maybe the problem emerged from declaring #ManyToOne(optional = true)
That is very weird.
In JPA nullable parameter is true by default. I use this kind of configuration all the time and it works fine. If you try to save entity it should be successful.
Did you try to delete table that is created for this relationship? Maybe you have legacy table with that column?
Or maybe you should try to find solution on other chunks of code, because this is proper configuration.
Note: I have tried this configuration on PostgreSQL with JPA2 and Hibernate.
EDIT
In that case maybe you can try a little bit different definition of primary key.
For example you can use definition like this:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column()
private Long id;
and postgresql will generate
id bigint NOT NULL
-- with constraint
CONSTRAINT some_table_pkey PRIMARY KEY (id)
If this is good enough you can try this solution.
within transaction but before the save operation, explicitly set the foreign key column value as null. By this hibernate ,never perform select queries for this foreign key related table and don't throw the exception "save the transient instance before flushing". if you want to set "null value " conditionally, then perform 1. fetch & set the value using repo call get/ find 2. then check the fetched value for the condition and set it to null accordingly .pasted the code below which is tested and found working
// Transaction Start
Optional<Customer> customerObject = customerRepository.findByCustomerId(customer.getCustomerId())
if(customerObject.isPresent())yourEnclosingEntityObject.setCustomer(customerObject)}
else {yourEnclosingEntityObject.setCustomer(null)}
yourEnclosingEntityObjectRepository.save(yourEnclosingEntityObject)
// Transaction End

JPA2: Can we use multiple #ElementCollection in an Entity?

Here is the stripped down version of my code:
#Entity
public class Item implements Serializable{
#Id
#GeneratedValue
private long id;
#ElementCollection(fetch=FetchType.EAGER ,targetClass=Cost.class)
#CollectionTable(name="ItemCost", joinColumns = {#JoinColumn(name="itemId")})
private Set<Cost> costs= new HashSet<Cost>();
#ElementCollection(fetch=FetchType.EAGER ,targetClass=ItemLocation.class)
#CollectionTable(name="ItemLocation", joinColumns = {#JoinColumn(name="itemId")})
private Set<ItemLocation> itemLocations;
}
Is the above code allowed? I have two embeddable classes Cost and ItemLocation that I am using with #ElementCollection.
Issue:
When I try to run a named query
#NamedQuery(name = "Item.findAll", query = "SELECT i FROM Item i")
I have strange behavior. The records in the second elementcollection (ItemLccation table) are getting doubled (inserted into the table).
What it comes to JPA 2.0, your code is allowed. It is perfectly legal to have more than one collections that are annotated with ElementCollection. Also, it most likely does not have anything to do with problem you have. By the way, to find out is that really your problem, had you tried your code without costs collection?
In which point exactly duplicates in this collection occur first time? If ItemLocation does not define equals&hashcode, duplicates can easily come as result of adding items by yourself.
Possibly you are facing this problem: Primary keys in CollectionTable and chancing type to list and adding #OrderColumn will help.

How to efficiently count related rows within a model using Doctrine2

I'm pretty new to Doctrine and wondering how to efficiently calculate the number of related objects there are for a particular model object.
I read here that it's not a great idea to use the entity manager within models so I'm wondering how I would query the database to find out without lazy loading all of the related models and doing a count().
I haven't really found a great answer yet, but it seems like this is a pretty fundamental thing?
For example
class House
{
/**
* #var Room
*/
protected $rooms
public function getRoomCount()
{
// Cant use entity manager here?
}
}
class Room
{
// Shed loads of stuff in here
}
Doctrine 2 will get counts for you automatically as association properties are actually Doctrine Collection objects:
public function getRoomCount()
{
return $this->rooms->count();
}
If you mark the association as eager, Doctrine will load the rooms whenever you query for house entities. If you mark them as lazy (the default), Doctrine won't load the rooms until you actually access the $this->rooms property.
As of Doctrine 2.1 you can mark associations as extra lazy. This means that calling $this->rooms->count() won't load the rooms, it will just issue a COUNT query to the database.
You can read about extra lazy collections here: http://www.doctrine-project.org/docs/orm/2.1/en/tutorials/extra-lazy-associations.html

Comparing entities while unit testing with Hibernate

I am running JUnit tests using in memory HSQLDB. Let's say I have a method that inserts some values to the DB and I am checking if the method inserted the values correctly. Note that order of the insertion is not important.
#Test
public void should_insert_correctly() {
MyEntity[] expectedEntities = new MyEntity[2];
// init expected entities
Inserter out = new Inserter(session); // out: object under test
out.insert();
List list = session.createCriteria(MyEntity.class).list();
assertTrue(list.contains(expectedEntities[0]));
assertTrue(list.contains(expectedEntities[1]));
}
The problem is I cannot compare expected entities to actual ones because the expected's id and the actual's id are different. Since setId() of MyEntity is private (to prevent setting id explicitly), I cannot set all of the entities' id to 0 and compare like that.
How can I compare two result set regardless of their ids?
I found this more practical. Instead of fetching all results at once, I am fetching results according to the criterias and asserting they are not null.
public void should_insert_correctly() {
Inserter out = new Inserter(session); // out: object under test
out.insert();
Criteria criteria;
criteria = getCriteria(session, 0);
assertNotNull(criteria.uniqueResult());
criteria = getCriteria(session, 1);
assertNotNull(criteria.uniqueResult());
}
private Criteria getCriteria(Session session, int i) {
Criteria criteria = session.createCriteria(MyEntity.class);
criteria.add(Restrictions.eq("x", expectedX[i]));
criteria.add(Restrictions.eq("y", expectedY[i]));
return criteria;
}
A stateful entity should not override equals -- that is, entities should be compared for equality by reference identity -- so List.contains will not work as you want.
What I do is use reflection to compare the fields of the original and reloaded entities. The function that walks over the fields of the objects ignores transient fields and those annotated as #Transient.
I don't find I need to ignore the id. When the object is first flushed to the database, Hibernate allocates it an id. When it is reloaded, the object will have the same id.
The flaw in your test is that you have not set transaction boundaries. You need to save the objects in one transaction. When you commit that transaction, Hibernate will flush the objects to the database and allocate their ids. Then in another transaction load the entities back from the database. You will get another set of objects that should have the same ids and persistent (i.e. non-transient) state.
I would try to implement Object.equals(Object) method in your MyEntity class.
List.contains(Object) uses Object.equals(Object) (Source: Java 6 API) to determine if an Object is in this list.
The method session.createCriteria(MyEntity.class).list(); returns a list of new instances with the values you inserted (hopefully).
So you need to compare the values. This is easily done via the implementation of Object.equals(Object).
Clarification edit:
You could ignore the ids in your equals method, so that the comparison only cares about "real values".
YAE (Yet Another Edit):
I recommend reading this article about the equals() method: Angelika Langer: Secrets Of Equal. It explains all background information very well.