Many to many relationship using Fluent Nhibernate Automapping - nhibernate

We are facing problem applying many-to-many relationship using fluent nhibernate automapping.
The simplified form of domain model are as follows:
public class Group
{
private readonly IList<Recipient> _recipients = new List<Recipient>();
public virtual IList<Recipient> Recipients
{
get { return _recipients; }
}
}
public class Recipient
{
private readonly IList<Group> _groups = new List<Group>();
public virtual IList<Group> Groups
{
get { return _ groups; }
}
}
As the code above describes that Group and Recipient are having many-to-many relationship.
We are using automapping feature of fluent nhibernate to map our domain model with database. So, we needed to use Convention for automapping.
Following is code we used for many to many convention:-
public class ManyToManyConvention : IHasManyToManyConvention
{
#region IConvention<IManyToManyCollectionInspector,IManyToManyCollectionInstance> Members
public void Apply(FluentNHibernate.Conventions.Instances.IManyToManyCollectionInstance instance)
{
if (instance.OtherSide == null)
{
instance.Table(
string.Format(
"{0}To{1}",
instance.EntityType.Name + "_Id",
instance.ChildType.Name + "_Id"));
}
else
{
instance.Inverse();
}
instance.Cascade.All();
}
#endregion
}
I found this solution here :
http://blog.vuscode.com/malovicn/archive/2009/11/04/fluent-nhibernate-samples-auto-mapping-part-12.aspx#Many%20to%20Many%20convention
But in above code while debugging both time for Recipients-> Groups and for Groups->Recipients instance.OtherSide is coming not null. The assumption was 1st time instance.OtherSide will be not null and second time it will be null as relationship is applied on one side so we will just apply inverse to that.
So it’s creating 2 mapping tables which are same.
It is load to database to have 2 tables of same schema. Even when I try to save our domain model to database using many to many relationship. It’s saving only 1 side i.e it saves Recipients in the Groups , But not saving Groups in Recipients.In database also it is having entry in only one mapping table not in both.
So , the question is Are we doing the right thing? If not then how to do it.

you could take inverse itself as a criteria
public void Apply(IManyToManyCollectionInstance instance)
{
Debug.Assert(instance.OtherSide != null);
// Hack: the cast is nessesary because the compiler tries to take the Method and not the property
if (((IManyToManyCollectionInspector)instance.OtherSide).Inverse)
{
instance.Table(
string.Format(
"{0}To{1}",
instance.EntityType.Name + "_Id",
instance.ChildType.Name + "_Id"));
}
else
{
instance.Inverse();
}
instance.Cascade.All();
}

Related

Spring Data Rest ResourceProcessor not applied on Projections

I am using a ResourceProcessor to add additional links to my resource object when listed in a collection or fetched individually. However, when I apply a projection (or an excerpt project) to my repository, the ResourceProcessor does not get run and thus my links for that resource do not get created. Is there a means to allow my custom resource links to be added to a resource regardless of how the resource content is projected?
I think this issue is describing your case:
https://jira.spring.io/browse/DATAREST-713
Currently, spring-data-rest does not offer functionality to solve your problem.
We are using a little workaround that still needs a separate ResourceProcessor for each projection but we do not need to duplicate the link logic:
We have a base class that is able to get the underlying Entity for a Projection and invokes the Entity's ResourceProcessor and applies the links to the Projection.
Entity is a common interface for all our JPA entities - but I think you could also use org.springframework.data.domain.Persistable or org.springframework.hateoas.Identifiable.
/**
* Projections need their own resource processors in spring-data-rest.
* To avoid code duplication the ProjectionResourceProcessor delegates the link creation to
* the resource processor of the underlying entity.
* #param <E> entity type the projection is associated with
* #param <T> the resource type that this ResourceProcessor is for
*/
public class ProjectionResourceProcessor<E extends Entity, T> implements ResourceProcessor<Resource<T>> {
private final ResourceProcessor<Resource<E>> entityResourceProcessor;
public ProjectionResourceProcessor(ResourceProcessor<Resource<E>> entityResourceProcessor) {
this.entityResourceProcessor = entityResourceProcessor;
}
#SuppressWarnings("unchecked")
#Override
public Resource<T> process(Resource<T> resource) {
if (resource.getContent() instanceof TargetAware) {
TargetAware targetAware = (TargetAware) resource.getContent();
if (targetAware != null
&& targetAware.getTarget() != null
&& targetAware.getTarget() instanceof Entity) {
E target = (E) targetAware.getTarget();
resource.add(entityResourceProcessor.process(new Resource<>(target)).getLinks());
}
}
return resource;
}
}
An implementation of such a resource processor would look like this:
#Component
public class MyProjectionResourceProcessor extends ProjectionResourceProcessor<MyEntity, MyProjection> {
#Autowired
public MyProjectionResourceProcessor(EntityResourceProcessor resourceProcessor) {
super(resourceProcessor);
}
}
The implementation itself just passes the ResourceProcessor that can handle the entity class and passes it to our ProjectionResourceProcessor. It does not contain any link creation logic.
Here is a generic solution:
#Component
public class ProjectionProcessor implements RepresentationModelProcessor<EntityModel<TargetAware>> {
private final RepresentationModelProcessorInvoker processorInvoker;
public ProjectionProcessor(#Lazy RepresentationModelProcessorInvoker processorInvoker) {
this.processorInvoker = processorInvoker;
}
#Override
public EntityModel<TargetAware> process(EntityModel<TargetAware> entityModel) {
TargetAware content = entityModel.getContent();
if (content != null) {
entityModel.add(processorInvoker.invokeProcessorsFor(EntityModel.of(content.getTarget())).getLinks());
}
return entityModel;
}
}
It gets links for original entities and adds them to corrseponding projections.

Setting foreign key constraint name using NHibernate / Fluent NHibernate

I am using NHibernate with Fluent NHibernate as the ORM of my application. I frequently use export schema to generate the database, and then compare with an existing database. Due to the fact that NHibernate generates the name of the constraints I get a lot of noise when comparing. All tables are marked as changed each time.
I'd like to stabilize the constraint names using some convention such as FK_TableName_ColumnName.
I cannot get this to work however.
I've got this so far:
public class ForeignConstraintNamingConvention : IReferenceConvention, IHasManyConvention, IHasManyToManyConvention
{
public void Apply(IManyToOneInstance instance)
{
instance.ForeignKey(GetForeignConstraintName(instance.Name, instance.Columns.First().Name));
}
public void Apply(IOneToManyCollectionInstance instance)
{
if (instance.OtherSide != null)
{
instance.OtherSide.ForeignKey(GetForeignConstraintName(instance.TableName, instance.OtherSide.Columns.First().Name));
}
}
private static string GetForeignConstraintName(string tableName, string columnName)
{
return string.Format("FK_{0}_{1}", tableName, columnName);
}
public void Apply(IManyToManyCollectionInstance instance)
{
if (instance.Relationship != null)
{
instance.Relationship.ForeignKey(string.Format("FK_{0}_{1}", instance.TableName, instance.Relationship.Columns.First().Name));
}
if (instance.OtherSide != null && instance.OtherSide.Relationship != null)
{
instance.OtherSide.Relationship.ForeignKey(GetForeignConstraintName(instance.OtherSide.TableName, instance.OtherSide.Relationship.Columns.First().Name));
}
}
}
But instance.OtherSide is often null, I assume this is because the reference is unidirectional most of the time, and I'm not interested in the other side of the relation, except that I need to use stable key names. Given that the "other side" is null, there is no way for me to set the foreign key constraint name for that key.
Does anyone have any idea how to achieve fully stable constraint names, ideally based on the actual columns / tables involved?

Hibernate - one table multiple entities

Firstly, I have read Hibernate - One table with multiple entities?.
However, I would like to map two entities to the same table, but I would like both of them to be entities, which I can select from. What I mean:
One table: Person (id, name, dateOfBirth, city, street, zipcode).
Two Entities: Person (id, name, dateOfBirth), Address (id, city,
street, zipcode).
So it's a 1:1 relationship between Entities, but still 1 table in DB.
If I do it using the proposed solution (component keyword) in the above link, I can't query Address directly (I can access it via Person entity). And I want to be able to do
session.createCriteria(Adres.class)
How do I do that?
UPDATE:
I tried the one-to-one association between entities, in Address mapping:
<one-to-one name="Person " class="model_mapowanie_xml.Person "/>
and in Person mapping:
<one-to-one name="Address" class="model_mapowanie_xml.Address "/>
Both classes have fields referring to the other one. Selecting records works fine for that. However, how can I add in one transaction a record using both entities? (Id is db-generated)
Address ad = new Address();
ad.setProperty("Sydney");
Person p = new Person();
p.setProperty("John");
p.setAddress(ad);
session.save(p);
and only Person part is saved, the address property remains empty.
This is very simple to achieve with JPA and Hibernate.
Let's assume you are using the following book database table:
Mapping entities
Now, you can map two entities: Book and BookSummary to this table.
First, we will create a BaseBook abstract class which will be extended by all entities:
#MappedSuperclass
public abstract class BaseBook<T extends BaseBook> {
#Id
#GeneratedValue
private Long id;
#NaturalId
#Column(length = 15)
private String isbn;
#Column(length = 50)
private String title;
#Column(length = 50)
private String author;
public Long getId() {
return id;
}
public T setId(Long id) {
this.id = id;
return (T) this;
}
public String getIsbn() {
return isbn;
}
public T setIsbn(String isbn) {
this.isbn = isbn;
return (T) this;
}
public String getTitle() {
return title;
}
public T setTitle(String title) {
this.title = title;
return (T) this;
}
public String getAuthor() {
return author;
}
public T setAuthor(String author) {
this.author = author;
return (T) this;
}
}
Now, the BookSummary entity simply extends the BaseBook superclass and adds no additional entity attribute.
#Entity(name = "BookSummary")
#Table(name = "book")
public class BookSummary extends BaseBook<BookSummary> {
}
On the other hand, the Book entity extends the BaseBook superclass and maps the properties attribute.
#Entity(name = "Book")
#Table(name = "book")
#TypeDef(
name = "jsonb",
typeClass = JsonBinaryType.class
)
#DynamicUpdate
public class Book extends BaseBook<Book> {
#Type(type = "jsonb")
#Column(columnDefinition = "jsonb")
private String properties;
public String getProperties() {
return properties;
}
public Book setProperties(String properties) {
this.properties = properties;
return this;
}
public ObjectNode getJsonProperties() {
return (ObjectNode) JacksonUtil
.toJsonNode(properties);
}
}
Persisting entities
This way, you can persist either a Book entity:
entityManager.persist(
new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea")
.setProperties(
"{" +
" \"publisher\": \"Amazon\"," +
" \"price\": 44.99," +
" \"publication_date\": \"2016-20-12\"," +
" \"dimensions\": \"8.5 x 1.1 x 11 inches\"," +
" \"weight\": \"2.5 pounds\"," +
" \"average_review\": \"4.7 out of 5 stars\"," +
" \"url\": \"https://amzn.com/973022823X\"" +
"}"
)
);
or a BookSummary:
entityManager.persist(
new BookSummary()
.setIsbn("978-1934356555")
.setTitle("SQL Antipatterns")
.setAuthor("Bill Karwin")
);
Fetching entities
You can fetch the BookSummary entity:
BookSummary bookSummary = entityManager
.unwrap(Session.class)
.bySimpleNaturalId(BookSummary.class)
.load("978-9730228236");
assertEquals(
"High-Performance Java Persistence",
bookSummary.getTitle()
);
or the Book entity if you want:
Book book = entityManager
.unwrap(Session.class)
.bySimpleNaturalId(Book.class)
.load("978-9730228236");
assertEquals(
"High-Performance Java Persistence, 2nd edition",
book.getTitle()
);
Conclusion
So mapping multiple entities to the same database table, not only that it allows us to fetch data more efficiently, but it also speeds up the dirty checking process as Hibernate has to inspect fewer entity properties.
The only drawback of using this approach is that you have to make sure you don’t fetch more than one entity type for the same database table record, as otherwise, this can cause inconsistencies when flushing the Persistence Context.
You should be able to do it using #Table annotation. These entites will be treated as different entites but will be mapped onto same table.
#Entity
#Table(name="PERSON_TABLE")
class Person {}
#Entity
#Table(name"PERSON_TABLE")
class Address {}
Edit:
If you want to save both entities in one transaction you either have to explicitly save them using Session or set cascade property to cascade operations on relationship. I guess you want to cascade operations on Address when you do something on Person. See CascadeType if you use annotations.
In your hbm it would look like
<one-to-one name="Person" class="model_mapowanie_xml.Person" cascade="all"/>

Single reference into multiple objects

I am a bit lot about what to do in an OO/DB relation...
Here is the DB model :
CREATE TABLE User
Id
CREATE TABLE Location
userId
// EDIT oups, wrong !
// placeId
// Should be :
seatId
CREATE TABLE Game
locationId
Now some code :
class User
{
private Location locations[]; // need this for several reasons...
public function loadFromDatabase()
{
// Load data from DB
// ...
result = DB::query("SELECT Id FROM Locations WHERE userId="+this->Id);
foreach(result)
{
l = new Location();
l->loadFromDatabase(result);
locations[] = l;
}
}
}
class Location
{
private User user;
public function loadFromDatabase()
{
...
}
}
class Game
{
private Location location;
public loadFromDatabase()
{
/*
Here comes the problem :
how to have a reference to a location
created by the User class ?
*/
}
}
A User play Games in several Locations.
EDIT : And for each location the user plays on seat. Or on another seat...
When I want to know where a game has been played I access Game.location. And when I want to know who played it, I access Game.location.user
Here is my problem : I want the Game.location to be the same reference to one of the User.locations and I do not know how to do this...
And, globally, I feel something wrong about my code...
Any help ?
Thanks
Since you have a placeId in your Location table, I assume there is a Place table which describes what the places actually are, while the Location table simply represents the many-to-many mapping between users and places.
In that case, Location doesn't need to have an Id of its own and doesn't need to be a class, but Place does.
To load just one instance of each object from the database, cache the instances in a static map inside each class.
class Place
{
// Static
private static Place loadedPlaces[];
public static function get(id)
{
if (!loadedPlaces[id])
{
loadedPlaces[id] = new Place(id);
loadedPlaces[id]->loadFromDatabase();
}
return loadedPlaces[id];
}
// Non-static
private id;
public function loadFromDatabase()
{
// ...
}
}
Then to get references to places for the properties of a user or a game, you just access them via the static method.
class User
{
public function loadFromDatabase()
{
result = DB::query("SELECT placeId FROM Locations WHERE userId="+this->Id);
foreach(result)
{
places[] = Place::get(result);
}
}
}
class Game
{
public function loadFromDatabase()
{
place = Place::get(place);
}
}
This uses:
Lazy initialization, because places are only loaded when they are needed.
Multiton pattern, because there is only one instance of each place by id.
Not quite a factory method, because there's no object hierarchy involved.

Fluent NHibernate Has Many to Many Convention Questions

I am new to fluent nhibernate and nhibernate. I want to write a fluent nhibernate autopersistence convention to handle creating the many to many mappings for my entities.
This is what I have right now:
using System;
using FluentNHibernate.Conventions;
using FluentNHibernate.Mapping;
namespace Namespace
{
public class HasManyToManyConvention : IHasManyToManyConvention
{
public bool Accept(IManyToManyPart target) {
return true;
}
public void Apply(IManyToManyPart target) {
var parentName = target.EntityType.Name;
var childName = target.ChildType.Name;
const string tableNameFmt = "{0}To{1}";
const string keyColumnFmt = "{0}Fk";
string tableName;
if (parentName.CompareTo(childName) < 0) {
tableName = String.Format(tableNameFmt, parentName, childName);
}
else {
tableName = String.Format(tableNameFmt, childName, parentName);
}
target.WithChildKeyColumn(String.Format(keyColumnFmt, childName));
target.WithParentKeyColumn(String.Format(keyColumnFmt, parentName));
target.WithTableName(tableName);
target.Cascade.All();
}
}
}
It seems to work, but I feel that there is a better way to do this.
Now my questions:
Do you have a better way to do this?
Do you usually want the Cascade behavior here?
Do I need to worry about something besides making sure both sides of this association end up with the same table name?