How to apply the same naming strategy to #Index columnList as for column? - hibernate-mapping

Since one of the newer releases of the JBPM was added an indexes internal classes:
#Entity
#Table(name="Attachment",
indexes = {#Index(name = "IDX_Attachment_Id",
columnList="attachedBy_id"),
#Index(name = "IDX_Attachment_DataId",
columnList="TaskData_Attachments_Id")})
#SequenceGenerator(name="attachmentIdSeq",
sequenceName="ATTACHMENT_ID_SEQ", allocationSize=1)
public class AttachmentImpl implements InternalAttachment {
...
#ManyToOne()
private UserImpl attachedBy;
...
}
attachedBy_id column from the index columnList named attached_by_id in the database.
Hibernate cannot recognize correct physical column name and throws an exception:
org.hibernate.AnnotationException: Unable to create unique key constraint (attachedBy_id) on table attachment: database column 'attachedBy_id' not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational types)
I can't change the code of the JBPM. I don't want to change the column name in the database as well (it doesn't help anyway), but I need to map attachedBy_id from #Index columnList to attached_by_id in some way. Is it possible to apply the same naming strategy to the index columnList?

You need to use #JoinColumn:
#ManyToOne
#JoinColumn(name = "attached_by_id")
private UserImpl attachedBy;
And you need to update the #Index as well:
#Index(name = "IDX_Attachment_Id", columnList="attached_by_id"

Related

Discriminator Based Multi-Tenancy Filtering Using Hibernate Search

I am attempting to add full text search to an entity using hibernate-search. Our schema uses discriminator based multi-tenancy, where each tenant is a park with an id. The model looks like this:
#Entity
#Indexed
public class ProductModel {
#Field
// park is the tenant
private Long parkId;
#Field(index = Index.YES, analyze = Analyze.YES)
#Analyzer(definition = "customanalyzer")
private String name;
#Field(index = Index.YES, analyze = Analyze.YES)
#Analyzer(definition = "customanalyzer")
private String description;
}
When performing full text search, I will always be filtering based on a parkId. Would it make sense to annotate the parkId with #Field, and then add that filter to the lucene query like so:
org.apache.lucene.search.Query luceneQuery = qb
.bool()
.must(qb.keyword().onFields("parkId").matching(parkIdFilter))
// any aditional queries, like on name, description
.must(qb.keyword().onFields(fields).matching(textQuery).createQuery())
.createQuery();
Or is there a better way to handle multi-tenancy using hibernate search with discriminator columns? I've seen the example mentioned in the docs but don't know how to apply that to my particular use case.
The easiest path would be to use Hibernate ORM's built-in multi-tenancy support, which is currently limited to either schema-based or database-based multi-tenancy. If you do, Hibernate Search will handle multi-tenancy automatically, no need for you to do anything special.
I must admit Hibernate ORM's doc is a bit puzzling, since the discriminator strategy is mentioned, but not implemented.
I think (not sure) that you can set the hibernate.multiTenancy property to DISCRIMINATOR, and the only effect will be that Hibernate ORM will require you to use tenant IDs every time you open a session.
If that is the case (you will have to check), then you could do just that: set the hibernate.multiTenancy property to DISCRIMINATOR, and make sure to pass the tenant IDs every time you create a session. Then Hibernate Search will handle multi-tenancy out of the box, no additional work needed. You will still have to implement the multi-tenancy yourself on the database side, but at least on the index side you won't have to do anything.
If you don't want to use the built-in feature, then yes, you will have to annotate the parkId with #Field. I would recommend avoiding manually creating a boolean query just for that, though; you can simply use full text filters.

Fluent NHibernate Mapping Single Column to Composite Key

I have a situation where i have defined an entity in my domain model in which I would like to expose a single id column.
public class OfferedProduct
{
public virtual string Id {get; set;}
//other properties
}
The legacy database table this will map to is
CREATE TABLE ProductGrouping
MemberNumber INT NOT NULL,
GroupId CHAR NOT NULL,
...
I dont want to compromise the domain model by introducing two properties and mapping them using the "CompositeId" construct.
CompositeId().KeyProperty(x => x.MemberNumber).KeyProperty(x => x.GroupId)
What I want ideally is to concatenate the two values in the form {MemberNumber}{GroupId} and expose this as the Id value. I would then use a Custom Type to handle how these values are concatenated when retrieved from the DB and broken apart when saving/selecting.
I have noticed that the "CompositeId" method does not allow a customType as with the standard "Id" call; but the "Id" method does not provide the ability to set multiple columns. I have seen examples where people have used "Map" to combine two columns using a custom type, but not for id values.
I have noticed the "CompositeId" has an overload that can take a custom identity class but I am unsure how to use it in this scenario.
CompositeId<OfferedProductIdentifier>(x => x.?)
Any help would be greatly appreciated.
in case someone comes here
CompositeId()
.KeyProperty(t => t.Id, c =>
c.Type(typeof(MyUserType)).ColumnName("MemberNumber").ColumnName("GroupId"));

NHibernate Validator and Schema Export question

I'm learning to use NHibernate validator and it's Fluent API (Loquacious).
I have noticed is that I can't set an integer property or nullable int property (int?) to be not nullable. Well, why not?
In a database, an integer column can have null values. Even worse, when I generate DDL using SchemaExport, the integer column wont be picking up that non-nullabity (unless I express it in the Nhibernate mappings).
If you specify the validators using ValidatorDef<>, this is detected by the the schema export, and you'll get the appropriate SQL definitions, example:
public class InvoiceValidationDef : ValidationDef<Invoice>
{
public InvoiceValidationDef()
{
...
Define(x => x.Description).NotNullable().And.MaxLength(255);
...
}
}
Results in
create table Invoices (
...
Description NVARCHAR2(255) not null,
...
)
You gave the answer already. The validator is not scanned by schema export. You have to use the mapping.
NHibernate Validator sits on top of NHibernate. It is used to validate entities against NHibernate mappings and custom rules. For configuring field properties, such as whether they are nullable, this is done in the NHibernate mappings as it affects not only the validations done, but also the generated SQL statements.

NHibernate: Violated not-null constraint when saving HasMany relation with Cascade=AllDeleteOrphan

My bean looks like this:
public class A {
...
[HasMany (MapType = typeof(B), Table = "B_table", ColumnKey = "A_object_id",
Fetch = FetchEnum.Join,
RelationType = RelationType.List, Index = "id",
Cascade = ManyRelationCascadeEnum.AllDeleteOrphan)]
IList<B> BList { get; set; }
...
}
and when perform Save on this bean I expect that beans of type B will be automatically
saved (and deleted on update) too. NHibernate surely is trying that, but it does so
with B_table.A_object_id set to NULL first and then NHibernate updates B_table setting the proper B_table.A_object_id value (that is: A.ID).
This is not what I want, as I have a NOT NULL constraint in the database.
My question is: how to make NHibernate automatically save the child objects with the proper ID set from the start? I know I can create A bean, save it, get it's brand new ID, create B beans, set their A_object_id and then save B beans... but it's a workaround.
Unidirectional relationships (in which only the parent knows about the child) always result in an update for setting the Id. I'm not sure why and it doesn't make a lot of sense to me either but that's just how NHibernate works.
You need to create a bidirectional relationship where the HasMany would have an Inverse = true and B would have a reference to class A in it (which should be populated when you add B to the A collection.

NHibernate Criteria against an <ANY> Mapping

I have a Project model that has a property of type IProjectWorker, this could either be a single User or a Team. In Castle ActiveRecord it's defined like this:
[Any(typeof(int), MetaType = typeof(string), TypeColumn = "WorkerType", IdColumn = "WorkerID", Cascade = CascadeEnum.None)]
[Any.MetaValue("USER", typeof(User))]
[Any.MetaValue("TEAM", typeof(Team))]
public IProjectWorker Worker { get; set; }
Now I need to be able to search for projects where the worker's name contains some text. My initial reaction was something like this:
query
.CreateAlias("Worker", "Worker")
.Add(Restrictions.InsensitiveLike("Worker.WorkerName", SearchText, MatchMode.Anywhere));
But this gives me an error-- "any types do not have a unique referenced persister". This makes sense, it doesn't know how to handle joining to the two different tables for the search.
Can I make two different aliases for each table and do a Restrictions.Or() across them? I tried it, but couldn't quite get it right. Or is there some other way to do this using criteria that I'm missing? Or am I going to have to use HQL instead?