Element Collection not retrieving from Database - java

I am working with the Spring Boot Jpa Data Structure.
I have after successful test runs without a database been able to run my code flawlessly. After having hooked up my Spring Boot Application to a MySQL database and saving values into it, I run into the following issue:
Upon retrieving the data from database after restarting my application the List with the #ElementCollection is empty. I have checked the database and the data is present within the database, all other data is also retrieved without error. The application produces no error other than a Null Pointer which can be traced back to the value of the list being null.
Here is the code which is failing:
#Entity
public class Entity extends SuperEntity {
#ElementCollection
#LazyCollection(LazyCollectionOption.FALSE)
private List<String> flags;
public Entity() {
super();
this.flags = new LinkedList<>();
}
public Entity(List<String> flags) {
super();
this.flags = flags;
}
public Entity(#NotNull FormEntity formEntity) {
super();
this.flags = formEntity.getFlags();
}
public List<String> getFlags() {
return flags;
}
public void setFlags(List<String> flags) {
this.flags = flags;
}
}
#Entity
public abstract class SuperEntity {
protected #Id UUID id = UUID.randomUUID();
public SuperEntity() {}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
}
Any help is greatly appreciated and any additional materials needed from my end will be provided.

Related

Entity properties are not changing after calling dao.findByID(id)

The question here is why the entity properties are not being saved after calling some setters of the entity. Usually when changing a property of a managed entity, it should propagate to the database.
Take a look at this example:
#Service
public class SystemServiceImpl implements SystemService {
#Autowired
private SystemDao systemDao;
#Override
#Transactional
public System replace(Long systemID) {
// External system to replace
System system = systemDao.findByID(systemID);
if (null != system) {
system.setName("Test"); // Calling findByID again shows that this call did not have any effect.
}
return system;
}
}
-
#Entity
#Table(name = "db.system")
public class System {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long systemID;
private String name;
#OneToMany(mappedBy = "system", fetch = FetchType.LAZY)
#JsonIgnore
private List<Customer> customers = new ArrayList<Customer>();
public Long getSystemID() {
return systemID;
}
public void setSystemID(Long systemID) {
this.systemID = systemID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Customer> getCustomers() {
return customers;
}
public void setCustomers(List<Customer> customers) {
this.customers = customers;
}
}
If I call systemDao.merge after the system.setName("Test") then it is saved to the database. I feel I should not have to call merge as this should be a managed entity.
I tried having the replace method both with #Transactional and without, and both are producing the same result.
Any ideas?
Thanks,
The behaviour can be explained if SystemDao.findByID() returns a detached object. Make sure that SystemDao does not use RequiresNew-Transaction or explicitly detach the object after loading it.
It would really help if you posted the code of SystemDao and any relevant configuration entries (Spring, entity manager config, ...).
The error was happening when I was running my integration tests. Turns out that Transactional is not doing anything because there is no session. The solution to this was to add a Transactional to my tests.

Observe removed objects in lists

We are currently using JaVers 3.0.0. Suppose we have the following two entities A and B. And A keeps track of some Bs in a list.
#Entity
#TypeName("A")
public class A {
#Id
private int id;
private List<B> items;
public A() {
items = new ArrayList<>();
}
public A(int id) {
this();
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<B> getItems() {
return items;
}
public void setItems(List<B> items) {
this.items = items;
}
}
And here is our rather simple class B.
#Entity
#TypeName("B")
public class B {
#Id
private int id;
public B() {
}
public B(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
Lets commit three changes to an object of type A:
Commit object with empty list
Commit object with two added Bs
Commit object with one B removed
After that I want to observe changes on B.
Javers javers = JaversBuilder
.javers()
.build();
A a = new A(1);
javers.commit("foo#example.com", a);
a.getItems().add(new B(1));
a.getItems().add(new B(2));
javers.commit("foo#example.com", a);
a.getItems().remove(0);
javers.commit("foo#example.com", a);
List<Change> changes = javers.findChanges(
QueryBuilder.byClass(B.class)
.build());
String changelog = javers.processChangeList(changes, new SimpleTextChangeLog());
System.out.println(changelog);
The output says zero changes. I was expecting to see a removed object since B is an entity and has an Id. What am I missing?
Edit
Thanks for answering in the comments so far. Maybe I wasn't detailed enough. Sorry about that.
What I am trying to query is all the changes on A and all the changes on B. I only commit A, but maybe that is the problem? Should I track A and B?
javers.compare() and javers.commit() don't work in the same way.
compare() simply compares two object graphs, without any context.
That's why you could expect ObjectRemoved on the change list when comparing graphs.
But commit() is for auditing changes.
Since you've mapped both classes as Entities, they are independent. B objects can't be marked as deleted just because they are no longer referenced by A objects.
The only way to mark them as deleted (and to have ObjectRemoved change) is by calling commitShallowDelete()

save mongo entity to the different collections

I've been using Spring Data for saving entities to the mongo DB and my code at the moment looks like this:
I have a repo class:
public interface LogRepo extends MongoRepository<Log, String> {
}
and I have an Entity Log which looks like this:
#Document(
collection = "logs"
)
public class Log {
#Id
private String id;
private String jsonMessage;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getJsonMessage() {
return jsonMessage;
}
public void setJsonMessage(String jsonMessage) {
this.jsonMessage = jsonMessage;
}
}
and this work well for me, however this approach works only for the case if I want to save Log entities to "logs" collection. However it would be very nice for me to be able to save Log entity to different collections depending on the context. I mean it would be nice to define collection name in the runtime. Is it possible somehow?
Thanks, cheers
Try to use inheritance and define appropriate collection names in such way. May give you possibility to save in different collections but you will be still not able to specify dynamically collection names and resp. their amount at runtime.
#Document(
collection = "logs"
)
public class Log {
#Id
private String id;
private String jsonMessage;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getJsonMessage() {
return jsonMessage;
}
public void setJsonMessage(String jsonMessage) {
this.jsonMessage = jsonMessage;
}
}
#Document(
collection = "log_child"
)
public class LogChild extends Log{}
With the MongoOperations save method you can choose which class to use and
based on the class it will choose the appropriate collection.
#Document(collection = "collection_#{T(com.github.your_project.DBUtils).getCollectionName()}")
public Class Collection
You can change the name in real time using a static getter
#UtilityClass
public class DBUtils {
private String collectionName;
public String getCollectionName() {
return collectionName;
}
public void setCollectionName(String collectionName) {
DBUtils.collectionName = collectionName;
}
}

JavaFX and Data Binding with JPA entities

As the question says, I would like to know the easiest way to perform Data Binding to regular (non JavaFX) properties of JPA entities.
I was thinking there is probably some way to use the same binding functions if you make your entity class implement some interface for change listeners or something of the sort.
Here is an example of a JPA entity with property changed listeners.
#Entity
public class Ticket {
#Id
#GeneratedValue()
private int id;
#ManyToOne()
private EntryGate entryGate;
#ManyToOne()
private ExitGate exitGate;
#Transient
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
public Ticket() {
}
public Ticket(EntryGate owner) {
this();
this.entryGate = owner;
}
public void addListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
public void removeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
public int getId() {
return id;
}
public EntryGate getEntryGate() {
return entryGate;
}
public void setEntryGate(EntryGate entryGate) {
EntryGate oldGate = this.entryGate;
this.entryGate = entryGate;
changeSupport.firePropertyChange("entryGate", oldGate, this.entryGate);
}
public ExitGate getExitGate() {
return exitGate;
}
public void setExitGate(ExitGate exitGate) {
ExitGate oldGate = this.exitGate;
this.exitGate = exitGate;
changeSupport.firePropertyChange("exitGate", oldGate, this.exitGate);
}
}
Here is an example of a JavaFX property binding.
this.idLabel.textProperty().bind(this.ticket.idProperty().asString());
Obviously I can't replace my JPA properties with SimpleXXXProperty... because they are entities participating in object relational mapping.

Save entity after unique constraint exception

i have an entity with a unique name.
In my example i save two persons with the same name. At the second time comes an "EntityExists" (Unique) Exception, that was the expected behavior.
After it i changed name and set the "ID" to null.
Than i try to persist it again but i get "org.apache.openjpa.persistence.EntityExistsException: Attempt to persist detached object "com.Person#1117a20". If this is a new instance, make sure any version and/or auto-generated primary key fields are null/default when persisting.
without the version it works but i find no solution to "reset" the version number.
Can someone help me?
Update: My new problem is, that i have a base entity an two pcVersionInit (look at my answer at bottom) i can't override it, i tried it in base and normal entity what is the best practise now instead of "override" the value in pcVersionInit ? Copy Constructor?"
public class Starter{
private static EntityManager em;
public static void main(String[] args) {
em = Persistence.createEntityManagerFactory("openjpa")
.createEntityManager();
Person p1 = new Person("TEST");
savePerson(p1);
Person p2 = null;
try{
p2 = new Person("TEST");
savePerson(p2);
}catch(Exception e){
p2.setId(null);
p2.setName(p2.getName()+"2");
em.persist(p2);
}
}
private static void savePerson(Person person){
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
}
}
Person.class:
#Entity
public class Person implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator="seqGenerator")
#SequenceGenerator(name="seqGenerator",sequenceName="personSeq")
private Long id;
#Version
private Long version;
#Column(nullable=true, unique=true)
private String name;
public Person(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
}
First off, stay away from messing around with pcVersionInit. I'd suggest to create a copy constructor in your Person Entity and in the event of rollback create a new one using the copy constructor.
Okay the problem is that OpenJPA adds a field named pcVersionInit (with #version) and set it "true" after try to persist. If i use reflection to set it to false, it works. Other way is a copy constructor.

Categories