DataNucleus, JDO, how to persist my own List implementation? - java

I have written my own java.util.List implementation, and now i want to store it in a MySQL using DataNucleus. My implementation consists of a public class that implements the List interface, and a private class that implements the node for that list.
When I run the SchemaTool in Eclipse, only the table for my Node implementation gets created, and when i run my app, i get the following error:
Persistent class "a.b.c.util.DtvList" has no table in the database, but the operation requires it. Please check the specification of the MetaData for this class.
Here's the beginning of my List implementing class...
#PersistenceCapable
#Inheritance(strategy=InheritanceStrategy.COMPLETE_TABLE)
public class DtvList<E extends Comparable<E>> implements List {
#Persistent
private DtvListNode first = null;
private DtvListNode last = null;
private int length = 0;
public DtvList(){};
Also, i only have an implementation for the add(E object) method, all the other methods throw a RuntimeException. Could that be the problem?
PS I also tried implementing some more methods, such as getIterator and others, and I even tried writing a mapping plugin (http://www.datanucleus.org/extensions/rdbms_java_types.html), but to no avail. The TABLE does not get created by the SchemaTool in the database.
PS/2 Added the Mapping class for the DtvListNode implementation, now i have a table for the DtvList, but not for the DtvListNode. It is still not working. But i still get the exception org.datanucleus.store.exceptions.NoTableManagedException that the DtvList table does not exist.

I don't think DataNucleus supports custom List implementation for mapping relationships.
If you Lists are small in size and your implementation can support a copy constructor and to List(), you could map a standard List and implement LoadCallback and StoreCallback to manage the conversion. Obviously if you have a lot of persistent operations on that List, it will get rather messy...

Related

Spring data elasticsearch:throws MappingInstantiationException: Failed to instantiate java.util.Set using constructor NO_CONSTRUCTOR with arguments

I am in process of migrating an application to the latest spring boot version (using gradle spring-boot-dependencies with version 2.5.4).
I have an entity called Customer which is annotated with #Document(indexName = "customer"); and another entity Address which is annotated with #Document(indexName = "address"). Customer class has private Set<Address> addresses = new HashSet<>().
Getting the below error, while calling List hits = elasticsearchTemplate
.search(nativeSearchQuery, Customer.class) from the repository.
org.springframework.data.mapping.model.MappingInstantiationException:
Failed to instantiate java.util.Set using constructor NO_CONSTRUCTOR with arguments.
It is working if I follow similar MONGO suggestion Failed to instantiate java.util.Set using constructor NO_CONSTRUCTOR with arguments and make the declaration private HashSet<Address> addresses = new HashSet<>(). I can't follow this as we have hundreds of entities and their JPA criteria mappings built with JPA Metamodel are failing.
please note that I have already reviewed the below 2 threads and feel it's different:
Indexing problem with Spring Data Elastic migration from 3.x to 4.x
Spring data elasticsearch: Using #Document annotation on POJO interface class not working
if you are looking for a sample repository: https://gitlab.com/mvallapuneni/spring-es-sample
Any help is appreciated...
java.util.Set is an interface not a class.
Interfaces do not have constructors, they are joined to a class that "implements" (has the operating code methods of) the interface.
All method signatures in an interface are abstract , they have no code, only the signature of methods supplied by the implementing class.
e.g. HashSet is a class that implements java.util.Set
See the API documentation at the top of its page for "all known implementing classes"
Your HashSet is private it must use private methods to call on it e.g. set or get e.t.c.
To hazard a guess the List must become a Set but List is instantiated and Set is a variable waiting.
Somewhere in the code in private methods this goes like (figuratively)...
private Set<Address> addresses = (Set<Address>)(Collection<Address>)elasticsearchTemplate.search(nativeSearchQuery, Customer.class);
Note that in that line they are all interfaces but to have an instantiated interface means there is a class attached that implements the interface List , however the class should also be able to use interface Set.
I just presume the List returned is actually in a HashSet and the instantiation is simply reserving space in memory like "C" language, just a helper that is not required to have any class instantiated at that time.
A HashSet takes a Collection in its constructor and a Collection can be either a Set or a List.

How to extend non-modifiable model to use with JPA?

What's the best practice to create persistence (say via Spring Boot or just JPA or Hibernate itself) for a data model coming from a non-modifiable dependency? Typical limitations like not being able to override a field or what patterns like Decorator allow and what not slowed my progress down. I tried some things, but I always end up with the result that it would be necessary to either modify the source model (like adding annotations to make it natively compatible -> the fork I don't want) OR write a ton of wrapper code which would replicate the original model too much - but even this isn't working right now:
I tried
Creating a JpaRepository for the original class. Doesn't work, because casting the extended class to its parent class is not working.
Extend the original class with a custom class that gets necessary annotations like #Entity can be used in such a repository. But problems here were
that the original class is missing an #Id annotation, which could be fixed by using a new ID in the extended class, but
the given model also has a non-simple architecture, including lists of other classes that are part of the model itself. So other annotations like #ElementCollection might be necessary, which can't be added because overriding of fields is not possible.
Hiding it with creating a new field with the same name in the new class is not working:
An error like Could not determine type for: java.util.List, at table: yeah_this_one, for columns:[org.hibernate.mapping.Column(objects)] indicates that the original field can't be hidden completely (changed table and column name in new class to verify that).
So of course adding #ElementCollection (which is said to solve that) isn't helping here, too.
#AttributeOverride is also not working to override annotations to set the ID or other settings, only the name and column can be changed.
I'm stuck at this state and am wondering if this is even the right approach at all.
The setup or what I would expect to work from my understanding:
The general idea is based on this Spring Boot REST tutorial, which I tried to expand with a model from a dependency.
Let's assume there is the original model class Model from a dependency that can not be modified. The ModelEntity would be the extended class to act as way to pull the model into Spring persistence.
In the scope of the dependency the original class would be like:
// Given dependency, not modifiable
#Some existing annotation
public class Model extends AnotherClassFromDep {
#more annotations
private IdLikeClassFromDep modelId;
//more complex attribute
#Nullable
private List<RefClassFromDep> objects = new ArrayList<>();
// more attributes, getter, setter etc.
}
In the scope of my program:
In combination with this little additional orm.xml it is possible to annotate the original Model as MappedSuperclass without modifying it (according to https://stackoverflow.com/a/2516951/1844976).
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0">
<mapped-superclass class="package.name.of.original.Model">
</mapped-superclass>
</entity-mappings>
This allows to create a class like this, which extends the original POJO model to add JPA annotations:
#Entity
public class ModelEntity extends Model {
// some #Id attribute is necessary, which should correspond to
// the already existing ID attribute from the original `Model`
// in the best case, but an additional one would work too
private #Id #GeneratedValue Long id;
// Different approaches to solve the List error from above, for
// instance hiding the original attribute
#ElementCollection
private List<RefClassFromDep> objects;
public ModelEntity(){
super();
}
}
At the current state the issues are blocking me from going further. But, altogether I would expect this to work with a JpaRepository:
// of course, creating a JpaRepository with original `Model` wouldn't
// work, because it has no `#Entity`
public interface ModelRepository extends JpaRepository<ModelEntity, IdLikeClassFromDep> {
}
In a way that actually accessing it like that is possible:
#Configuration
public class LoadDatabase {
#Bean
CommandLineRunner initDatabase(ModelRepository modelRepository) {
return args -> {
// depending on the implementation above, either create a
// Model and cast it or directly create a ModelEntity, set
// attriubtes and save it through the JpaRepository
modelRepository.save(model);
};
}
}
Both more abstract and specific code-related ideas and comments would help me. Thanks!
In the old days, Jpa/Hibernate were configured via XML.
You needed to provide persistence.xml for general configuration. In this file, you added <mapping-file> tag pointing to another file orm.xml In this file you configured mapping for your entities (which is done via JPA annotations these days).
See https://vladmihalcea.com/how-to-use-external-xml-mappings-files-outside-of-jar-with-jpa-and-hibernate/
While the methods described above are considered legacy, they are still supported. LocalContainerEntityManagerFactoryBean has method setMappingResources allowing you to point to the orm.xml file. There is some funkiness about search paths and default locations, but it is well documented:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.html#setMappingResources-java.lang.String...-
Note that the third-party class you are configuring this way needs to conform to Java Beans conventions (no-args constructor, getters and setters)

How to benefit from Spring/JPA for executing parametrized queries without referring to an entity

Basically I am looking for a nice mechanism to do something like
#Query(value ="generateReport", nativeQuery = true)
public void generateCSVReport(Path filename, UUID managerUuid);
where generateReport is a parametrized query located in #EnableJpaRepositories(namedQueriesLocation = "classpath:/foo/bar/file.sql)
that includes a
COPY ( SELECT * FROM foo) TO file WITH (FORMAT CSV)
Without the need of defining a Repository. Indeed I tried JpaRepository<Void,Void> but it does not work
If you don't have any entity attached to your repository then you don't really need to be implementing JpaRepository.. or even CrudRepository since it doesn't make sense for you to have any CRUD operations without an entity.
Try implementing the base interface Repository<T, ID extends Serializable> instead.
Also Repository<Void, Void> wont work since Void does not extend Serializable and also because Void is not a managed type (i.e. it is not an #Entity).
Using Spring Data repositories to do this you would have to create an empty dummy entity just to pass in. It would probably make sense to map this to the foo table you are querying in your SQL:
#Entity
#Table(name = "foo")
public class DummyEntity extends Serializable {
//Blank
}
Then extend Repository<DummyEntity, Integer>. This probably indicates that Spring Data repos aren't the best solution for this problem though.

Lazy Loading in custom ORM

I am currently working on developing a simple Java web application without using an ORM.
It is a layered architecture. I use annotations to 'map' the columns from my domain class to my persistence layer where the repositories are.
My DefaultRepository is using reflection to get the field names and build the queries. I just use this one instead of making a separate one for each domain (such as user, order, product).
It looks like this:
Repository:
public abstract class DefaultRepository <TYPE extends DefaultDomain<TYPE>>{
public int insert(Connection con, TYPE entity){};
public int update (Connection con, TYPE entity){};
public int delete (Connection con, TYPE entity){};
public int findById(Connection con, int id){};
...
}
I want to implement lazy loading to load the orders of each user only when requested. This preferably happens in the repository in the findById() where I map the result set to my TYPE (domain class).
How would I go about implementing this foreign key relation/ one to many/many to one in general?
An idea was to make an annotation with the specific repository and then call the findAll() method in there when the order list is called.
For example:
public class Order extends DefaultDomain{
#ManyToOne(repository=UserRepository.class)
private User user;
}
Is that the right way? Is there a different/better approach to this?

Extend entity classes with composite keys in hibernate

In our company we have a strange database model which can't be modified because to many systems works with them. Up to know we have a straight java application which connects with hibernate to the database and loads the data. We have for each table one xml mapping file.
The strange thing about the database is that we do not have any primary keys. Most table have a unique index containing several columns.
Now we want to use an application server (jboss) and the ejb model. So I created a class like this:
#Entity
#Table (name = "eakopf_t")
public class Eakopf implements Serializable {
#Embeddable
public static class EakopfId implements Serializable {
private String mandant;
private String fk_eakopf_posnr;
// I removed here the getters and setters to shorten it up
}
#Id
private EakopfId id;
private String login;
// I removed the getters and setters here as well
}
This works perfect.
Because our customers have different versions of the database schema I thought about extending this class on each database release change. So each interface we create with java can decide which version of the table will be used.
Here is the extended table class
#Entity
#Table (name = "eakopf_t")
public class Eakopf6001 extends Eakopf implements Serializable {
private String newField;
// getters and setters
}
If I use Eakopf (the base version) it is working if I do something like that:
EakopfId id = new EakopfId();
id.setMandant("001");
id.setFk_eakopf_posnr("ABC");
Eakopf kopf = (Eakopf) em.find(Eakopf.class, id);
But if I do this:
EakopfId id = new EakopfId();
id.setMandant("001");
id.setFk_eakopf_posnr("ABC");
Eakopf6001 kopf = (Eakopf6001) em.find(Eakopf6001.class, id);
this exception occues
javax.ejb.EJBException: javax.persistence.PersistenceException:
org.hibernate.WrongClassException: Object with id:
de.entity.Eakopf$EakopfId#291bfe83 was not of the specified subclass:
de.entity.Eakopf (Discriminator: null)
Does anybody has an idea?
many greetings,
Hauke
Doing what you did means to Hibernate that you're storing two different kinds of entities in a single table. This is possible is you use a discriminator column. But if I understand correctly, you just want one kind of entity in the table : Eakopf6001. In this case, its base class should be annotated with #MappedSuperClass, not with #Entity.
I would suggest creating a class annotated with #MappedEntity (let's call it BaseEakopf), and two entities: EaKopf and EaKopf6001, each with their set of additional fields. Include one of the other of the entities in the list of mapped classes, depending on which one you want to use.
My personal opinion is that if you have multiple versions of your app, they should use the same entities, but with different fields. Your version control system would take care of these multiple versions, rather than your source code (i.e. have one set of source files per version of the app, rather than one single set of source files for all the possible versions).

Categories

Resources