I'm trying to add JPA annotations to my model.
Here's a part of my superclass:
#Entity
#Table(name="destinations")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type")
public abstract class Destination {
protected abstract Destination getParent();
protected abstract Set<Destination> getChildren();
}
And these are the implementations:
#Entity
#DiscriminatorValue(value = "country")
public class Country extends Destination {
private Set<Destination> regions;
private Set<Destination> cities;
#Override
public Destination getParent() {
return null;
}
#Override
public Set<Destination> getChildren() {
if (regions != null && !regions.isEmpty()) {
return regions;
}
return cities;
}
}
#Entity
#DiscriminatorValue(value = "region")
public class Region extends Destination {
private Set<Destination> cities;
private Country country;
#Override
public Destination getParent() {
return country;
}
#Override
public Set<Destination> getChildren() {
return cities;
}
}
#Entity
#DiscriminatorValue(value = "city")
public class City extends Destination {
private Country country;
private Region region;
#Override
public Destination getParent() {
if (region != null) {
return region;
}
return country;
}
#Override
public Set<Destination> getChildren() {
return null;
}
}
So there are Countries that have Regions with Cities in it, and Countries without Regions but with Cities.
How do I annotate these classes so their bidirectional relationships can be persist and all the Destinations are in a single table?
Ideally as my understanding the design should have been this way
Country <----> City
Country <----> Region
City <----> Region
Considering that region can be or cannot exist we need to have Country to City mapping mandatory and optional mapping related to Region.
Now as per your requirement where in you need all of them associated to Destination which means as per the current design shown above you will have multiple values
for eg. Country(IND)-Region(MH)-City(MUM); Then you would 2 rows in destination one for Country-City and other Country-Region
Hence my final conclusion is that if you go for Inheritance Design you will end up having multiple rows as explained in the previously example and if you simply use OneToOne/OneToMany mapping as shown in point you persist twice once for Country-City and Country-Region
Related
As the title suggests: I am currently migrating from Hibernate 3.6 to 4.2.
PROBLEM
After the upgrade, Hibernate started to automatically generate a foreign key that points to the same table. Now, when trying to persist a new customer, a ConstraintViolationException is thrown.
DETAILS
Here's a picture of what I mean:
Here is the code of the corresponding class:
#Entity
#Table(name = "customers")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Customer extends AbstractModel {
public Customer(final Provider provider) {
this.provider = provider;
}
#Required
#ManyToOne(cascade = CascadeType.REFRESH)
public Provider provider;
#MaxSize(1023)
public String note;
public String getNote() { return note; }
...
}
As you can see, it extends from AbstractModel which is a #MappedSuperclass and contains the id property which serves as a primary key for all of our model classes. It's a #GeneratedValue.
It's a superclass itself. I don't know whether it's important so I am just going to include the code and schema of one of its child classes:
And the corresponding code:
#Entity
#Table(name="unregistered_customers")
public class UnregisteredCustomer extends Customer {
#MaxSize(MAX_SIZE_OF_NAMES_AND_IDENTIFIERS)
#Column(nullable = false, length = MAX_SIZE_OF_NAMES_AND_IDENTIFIERS)
public String userName;
#Email
#MaxSize(MAX_SIZE_OF_EMAIL_ADDRESSES)
#Column(unique = false, length = MAX_SIZE_OF_EMAIL_ADDRESSES)
public String email;
#MaxSize(MAX_SIZE_OF_NAMES_AND_IDENTIFIERS)
#Column(length = MAX_SIZE_OF_NAMES_AND_IDENTIFIERS)
public String mobile;
public UnregisteredCustomer(final Provider provider) {
super(provider);
}
RESEARCH
I already looked into the migration guide but nothing in there seems to be related to my issue.
I also looked into the Hibernate documentation especially into the section dealing with #Inheritance. Sadly, I didn't find anything concerning auto generated foreign keys.
QUESTION
How do I stop Hibernate from adding this self-referencing foreign key?
EDIT 1
As requested, the super class:
#MappedSuperclass
public abstract class AbstractModel extends AbstractBaseModel {
#Id
#GeneratedValue
public Long id;
public Long getId() {
return id;
}
#Override
public Object _key() {
return id;
}
}
Also, the super class of the super class:
#MappedSuperclass
public abstract class AbstractBaseModel extends GenericModel {
public static final int MAX_SIZE_OF_NAMES_AND_IDENTIFIERS = 80;
public static final int MAX_SIZE_OF_COMMENTS_AND_DESCRIPTIONS = 5000;
public static final int MAX_LIST_SIZE = 30;
public static final int MAX_SIZE_OF_EMAIL_ADDRESSES = 255;
public static final int MAX_SIZE_OF_JSON_CONTENT_FIELDS = 65535;
#Column(nullable = false, unique = true)
public String uuid;
#Column(nullable = false)
public Long created;
public DateTime getCreatedAsDate() {
return DateUtil.dateTimeWithSystemTimezone(created);
}
#Column(nullable = false)
public Long lastModified;
#PrePersist
protected void prePersist() {
final Long nowInMillis = DateUtil.dateTimeWithSystemTimezone().getMillis();
if (uuid == null) {
uuid = UuidUtil.newUUIDAsString();
Logger.trace("Created new Uuid for entity: %s", uuid);
}
if (created == null) {
created = nowInMillis;
}
if (lastModified == null) {
lastModified = nowInMillis;
}
}
#PreUpdate
protected void preUpdate() {
lastModified = DateUtil.dateTimeWithSystemTimezone().getMillis();
}
#Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
final AbstractBaseModel other = (AbstractBaseModel) obj;
return new EqualsBuilder().append(uuid, other.uuid).isEquals();
}
#Override
public int hashCode() {
return new HashCodeBuilder().append(uuid).toHashCode();
}
}
Put #Inheritance(strategy = InheritanceType.JOINED) to the parent entity (AbstractModel) not to the child. And customize the foreign key constraint using #PrimaryKeyJoinColumn at your child entity. Check out Part 4 (Joined Table) at https://www.baeldung.com/hibernate-inheritance to see it more clearly!
I am working on a project with GraphQL-java and Hibernate with MariaDB.
In my current solution, I get 18938 results back. I just want to see the last 10 of these. So I am looking for a solution to limit the number of results.
On the internet I see examples of limiting the number of results (https://graphql.org/learn/pagination/). They call it pagination. However, I cannot find the server implementation of this. Does anyone have experience with this?
I have an Entity class, with some properties : Test.java
#Entity
#Table(name = "test")
public class Test {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(max = 64)
#Column(nullable = false)
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "parent")
private Test parent;
public Test() {
}
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 Test getParent() {
return parent;
}
public void setParent(Test parent) {
this.parent = parent;
}
My repository class: TestRepository.java
public interface TestRepository extends CrudRepository<Test, Integer> {}
My GraphQL resolver class: Query.java
#Component
public class Query implements GraphQLQueryResolver {
private TestRepository testRepository;
#Autowired
public Query(TestRepository testRepository) {
this.testRepository = testRepository;
}
public Iterable<Test> findAllTests(Integer first) {
return testRepository.findAll();
}
public long countTests() {
return testRepository.count();
}
}
My GraphQL schema: test.graphqls
type Test {
id: ID!
name: String!
parent: Test
}
#extend query
type Query {
findAllTests(first: Int): [Test]!
countTests: Int!
}
To summarize my last comment here is what I would do:
Instead of extending CrudRepository, extend PagingAndSortingRepository (which is extending CrudRepository)
public interface TestRepository extends PagingAndSortingRepository<Test, Integer> {
}
In your Query class pass two args to findAllTests method, page and size that will be used to create the Pageable object
#Component
public class Query implements GraphQLQueryResolver {
// other properties & methods are omitted for brevity
public Iterable<Test> findAllTests(Integer page, Integer size) {
Pageable pageable = PageRequest.of(page, size);
return testRepository.findAll(pageable).getContent(); // findAll returns Page and we can get the underlying List with getContent
}
}
Add two params from above in your GraphQL schema (I set default page size to be 20)
#extend query
type Query {
findAllTests(page: Int = 0, size: Int = 20): [Test]!
countTests: Int!
}
Since I have no experience with GraphQL, I'm not sure if this works, but you can give me feedback if there are some problems.
I try to map my DTO objects to my JPA entities. I have a Collection of children in my ParentEntity. They can be added addChild(). Using the Adder is supported by Mapstruct via the CollectionMappingStrategy (http://mapstruct.org/documentation/dev/reference/html/#collection-mapping-strategies).
This works fine if I create new entities, but fails to clear the children on updating before adding the new children.
The Mapstruct manual says (http://mapstruct.org/documentation/dev/reference/html/#updating-bean-instances):
Collection- or map-typed properties of the target bean to be updated will be cleared and then populated with the values from the corresponding source collection or map.
What am I missing? Is there an additional option I have to set? There is a full example with test case to reproduce the problem at https://github.com/davidfuhr/mapstruct-jpa-child-parent
Here are the classes:
public class ParentEntity {
private String name;
private List<ChildEntity> children = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ChildEntity> getChildren() {
return children;
}
public void addChild(ChildEntity child) {
children.add(child);
child.setMyParent(this);
}
public void removeChild(ChildEntity child) {
children.remove(child);
child.setMyParent(null);
}
}
public class ChildEntity {
private String name;
private ParentEntity myParent;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ParentEntity getMyParent() {
return myParent;
}
public void setMyParent(ParentEntity myParent) {
this.myParent = myParent;
}
}
public class ParentDto {
private String name;
private List<ChildDto> children;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ChildDto> getChildren() {
return children;
}
public void setChildren(List<ChildDto> children) {
this.children = children;
}
}
public class ChildDto {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED)
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper(SourceTargetMapper.class);
ParentEntity toEntity(ParentDto s);
ParentEntity updateEntity(ParentDto s, #MappingTarget ParentEntity e);
#Mapping(target = "myParent", ignore = true)
ChildEntity toEntity(ChildDto s);
}
The text in the documentation need to be rephrased. The problem is that especially for collections, there's no good way to handle this out of the box in MapStruct. I'm currently writing some new text for the documentation.
Consider this (when thinking what MapStruct should do for updating collections in general):
What if there's no match: should the non-matching elements be removed?
Should the non matching source elements be added?
What exactly constitutes to a match: equals? hashcode? comparator==0?
Can there be more than one match (Lists, but also depending on what is considered a match.)
How should the resulting collection be sorted?
Should a newly created object be added to a persistence context?
What about JPA child-parent relations?
About the latter one, Dali (Eclipse) also generates remove methods. So should MapStruct call these in the light of the above?
At this moment it works like this: whenever the user wants a collection update method, MapStruct generates a regular call to element mappings (in stead of an update call), because it is the only sensible thing to do. All the remainder is highly dependent on the use-case. If you need to clear the collection at before hand, use the #BeforeMapping to clear it.
Note: I just fixed an issue that handles also adders in this fashion in stead of the vague error message you get now.
If you want a nice way to handle child/parent relations and integrate them with JPA.. have a look at the examples.
I'm using Neo4j ogm to map many (over 20) similar classes into neo4j db which are different in just
relationship name, name and direction.
Each class implements the "Classification" interface with just one method which is the same
in every class (consist on adding relation into collection of node)
Example node:
#NodeEntity
public class ExampleClass implements Classification {
#GraphId
private Long id;
private String name;
#Relationship(type = "EXAMPLE_1", direction = "OUTGOING")
private Set<Species> classification = new HashSet<>();
public ExampleClass (){}
public ExampleClass (String name) {
this.name = name;
}
public Set<Species> getClassification(){
return classification;
}
#Override
public void specifiedAs(Species species){
classification.add(species);
}
and analogously:
#NodeEntity
public class ExampleClass2 implements Classification {
#GraphId
private Long id;
private String name;
#Relationship(type = "EXAMPLE_2", direction = "OUTGOING")
private Set<Species> classification = new HashSet<>();
public ExampleClass2 (){}
public ExampleClass2 (String name) {
this.name = name;
}
public Set<Species> getClassification(){
return classification;
}
#Override
public void specifiedAs(Species species){
classification.add(species);
}
}
I'm looking for possibility to reduce count of those similar classes
and create... maybe one generic class in which I can define label,property name and realtionship type also.
I prefer still using spring-data and Neo4j OGM.
You could improve this by introducing an super class containing all the common properties, and just have the specific relationships in your ExampleClassXXX classes.
Note that the relationship types cannot be dynamic, so you cannot have just a generic class by itself.
Consider the diagram:
I've been working with JPA for a short time and, so far, I have never had the need to persist extended classes... As you can see by the example, SNMPNode, IPNode, etc are all extended classes from Node that is also extended from GeoLocation.
I understand that I can annotate the master classes with #MappedSuperclass and IPNode and SNMPNode will inherit their properties for persisting... But in this scenario I will end up with nearly identical tables and, to my understanding, instead of doing that I could just group all information in Node and work with a single table.
Is this the way persistence of extended classes on JPA work or my concepts are wrong?
Same thing as a resumed piece of code:
public class Node extends GeoLocation {
private String name;
private Group group;
private Location location;
private Type type;
private Company company;
}
public class IPNode extends Node {
private Long id;
private String ipAddress;
}
public class SNMPNode extends Node {
private Long id;
private SNMPServer server;
}
[[ EDITED AFTER ANSWER FROM THIS POINT ]]
For the sake of contributing, here's a sample of what I end up doing:
INode:
public interface INode {
public Long getId();
public void setId(Long id);
public String getName();
public void setName(String name);
public String getIpAddress();
public void setIpAddress(String ipAddress);
public String getCommunity();
public void setCommunity(String community);
}
Node:
#Entity
#DiscriminatorValue("N")
#DiscriminatorColumn(name="NODE_TYPE",discriminatorType=DiscriminatorType.STRING, length=20)
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Node extends GeoLocation implements INode {
#Id
#GeneratedValue
private Long id;
private String 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;}
(... Overrides from INode ...)
}
IPNode:
#Entity
#DiscriminatorValue("I")
public class IPNode extends Node {
private String ipAddress;
public String getIpAddress() { return this.ipAddress;}
public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; }
(... Overrides from INode ...)
}
SNMPNode:
#Entity
#DiscriminatorValue("S")
public class SNMPNode extends Node {
private String community;
public String getCommunity() { return community;}
public void setCommunity(String community) { this.community = community; }
(... Overrides from INode ...)
}
NodeRepository:
#Repository
public interface NodeRepository extends JpaRepository<Node, Long> { }
So now I can do stuff like this:
#ContextConfiguration("classpath:/spring/application-context.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class NodeRepositoryTest {
#Autowired
NodeRepository repo;
private INode node;
#Before
#Transactional
#Rollback(false)
public void setup() {
node = new IPNode();
node.setName("ipNode");
node.setIpAddress("1.1.1.1");
repo.save((IPNode)node);
node = new SNMPNode();
node.setName("snmpNode");
node.setIpAddress("2.2.2.2");
node.setCommunity("some text");
repo.save((SNMPNode)node);
}
#Test
#Transactional
public void Test() throws Exception {
INode testNode = repo.findOne(1L);
assertNotNull(testNode);
}
}
Both Node types are saved on the same table and so their keys can't repeat... My REST URL can grab them by /nodes/1 or /nodes/2, which was my main goal after all...
Thanks :)
If your base class is annotated with #MappedSuperclass then inheritance is only relevant in the OOP context. The #MappedSuperclass properties are simply copied to each sub-class associated database table and you can only query for sub-class entities.
Single table inheritance yields the best performance (no join or union is involved) at the price of not being able to declare not-nullable all specific sub-class properties (since all base and all sub-class specific properties go to a single database table).
With joined inheritance tables you can have the base class properties in a base database table, and each specific sub-class has it's own associated table. The sub-class table is linked with the base database table through a FK, so you need to join these tables to fetch a sub-class entity. As opossed to #MappedSuperclass, the base class is queryable since both the OOP context and the database reflect the inheritance model.