When there is an #EmbeddedId field, a custom Field Bridge should be implemented.
There is a Feature opened about it https://hibernate.atlassian.net/browse/HSEARCH-1879. But it isn't ready yet.
In this case, the interface correct to implement is TwoWayFieldBridge?
Below is my implementation for a composite ID with 5 fields.
public class ChavePrimariaAcompanhamentoBridge implements TwoWayFieldBridge {
#Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
AcompanhamentoPK chavePrimaria = (AcompanhamentoPK) value;
Integer ano = chavePrimaria.getAno();
Integer mes = chavePrimaria.getMes();
Long codigoCredenciada = chavePrimaria.getCredenciada().getCodigo();
Long codigoPosto = chavePrimaria.getPostoAtendimento().getCodigo();
Integer numeroSequencial = chavePrimaria.getNumeroSequencial();
luceneOptions.addNumericFieldToDocument("mes", mes, document);
luceneOptions.addNumericFieldToDocument("ano", ano, document);
luceneOptions.addNumericFieldToDocument("credenciada.codigo", codigoCredenciada, document);
luceneOptions.addNumericFieldToDocument("postoAtendimento.codigo", codigoPosto, document);
luceneOptions.addNumericFieldToDocument("numeroSequencial", numeroSequencial, document);
}
#Override
public Object get(String name, Document document) {
AcompanhamentoPK chavePrimaria = new AcompanhamentoPK();
chavePrimaria.setMes(Integer.valueOf(document.get("mes")));
chavePrimaria.setAno(Integer.valueOf(document.get("ano")));
chavePrimaria.setCredenciada(new Credenciada(Long.valueOf(document.get("credenciada.codigo"))));
chavePrimaria.setPostoAtendimento(new CadastroPostoAtendimento(Long.valueOf(document.get("postoAtendimento.codigo"))));
chavePrimaria.setNumeroSequencial(Integer.valueOf(document.get("numeroSequencial")));
return chavePrimaria;
}
#Override
public String objectToString(Object value) {
AcompanhamentoPK chavePrimaria = (AcompanhamentoPK) value;
return chavePrimaria.toString();
}
}
1 - Is there another best way to make it?
2 - Is there any error(about concepts) in this implementation?
3 - What is the objectToString method used for? It is important?
I am making this question because I haven't found any documentation about it, so I am not sure.
EDIT: In Hibernate Search 6+, in order to map an #EmbeddedId, you should use a custom IdentifierBridge: https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#mapper-orm-bridge-identifierbridge
Original answer for Hibernate Search 5:
In this case, the interface correct to implement is TwoWayFieldBridge?
Yes: https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#_two_way_bridge
1 - Is there another best way to make it?
Not that I know of.
2 - Is there any error(about concepts) in this implementation?
Yes.
You shouldn't use Integer.valueOf here. Just call Field.numericValue() on the result of document.get, and cast the result to Integer.
You should also store the unique string representation of the ID in the set() method:
luceneOptions.addFieldToDocument( name, objectToString( id ), document );
3 - What is the objectToString method used for?
Hibernate Search will use the result of this method mostly to build queries, for example when it must retrieve documents that should be deleted, or when you query the ID field explicitly.
It is important?
Nothing will work unless you implement it properly, i.e. unless you make sure that:
it returns two different values for two different composite IDs
it always returns the same value for a given composite ID
Related
I am using Spring Data JPA and QueryDsl (v.4.2.2), Java 8. I can explicitly construct search predicates and pass them to the repository methods. However, I like the idea of using the #QuerydslPredicate annotation on a web/REST controller's method argument when the queried entities have more than a few properties, and I want the flexibility of filtering the search by any of them. So, something like this, generally, works very well:
#GetMapping("/accounts/summaries")
public PageDto<AccountSummaryDto> getAccountSummaries(#QuerydslPredicate(root = AccountSummary.class) Predicate accountSearchPredicate,
#RequestParam(name = "pageIndex", defaultValue = "0") int pageIndex,
#RequestParam(name = "pageSize", defaultValue = "25") int pageSize,
#RequestParam(name = "sortBy", defaultValue = "id") String sortBy,
#RequestParam(name = "sortOrder", defaultValue = "desc") String sortOrder) {
// delegating to web-agnostic service that:
// - creates Pageable pageRequest,
// - calls accountSummaryRepository.findAll(predicate, pageRequest),
// - constructs custom PageDto wrapper, etc.
return accountService.retrieveAccountSummaries(accountSearchPredicate, pageIndex, pageSize, sortBy, sortOrder);
}
My Spring Data JPA repository interface looks similar to this:
public interface AccountSummarySearchRepository
extends JpaRepository<AccountSummary, Integer>, QuerydslPredicateExecutor<AccountSummary>, QuerydslBinderCustomizer<QAccountSummary > {
#Override
default void customize(QuerydslBindings bindings, QAccountSummary acctSummary) {
bindings.bind(acctSummary.customer.firstName).first((path, value) -> path.isNull().or(path.startsWithIgnoreCase(value))) ;
bindings.bind(acctSummary.customer.lastName).first((path, value) -> path.isNull().or(path.startsWithIgnoreCase(value))) ;
// etc.
// default binding for String properties to be case insensitive "contains" match
bindings.bind(String.class).first(
(StringPath path, String value) -> path.isNull().or(path.containsIgnoreCase(value)));
}
My question:
The bindings in the customize method are set using the entity field
paths and the values of the request parameters that match those
paths. If the parameter is not specified, is there a way to bind the
path to some constant value or a value obtained dynamically?
For example, I want to always ONLY retrieve the entities where property deleted is set to false - without forcing the client to pass that as a query parameter? Similarly, I may want to set other default lookup values dynamically for each query. For example, I may want to "retrieve only those accounts where assignedTo == [current user ID available on a ThreadLocal]...
The following will not work
bindings.bind(acctSummary.deleted).first((path, value) -> path.eq(false));
because it, obviously, expects the first occurrence of the path/value pair for deleted=... in the Predicate (mapped from the incoming request params via the #QuerydslPredicate annotation. I don't want to pass that as a parameter because the requester does not even need to know about the existence of such field.
Is there a simple way to infuse the Predicate instance that is auto-populated via the #QuerydslPredicate annotation with any additional implicit/default criteria that are not explicitly passed in the web request? Could this be done in the customize method? I suppose, one (very ugly) way would be to intercept the HTTP request in a filter - before it is processed by the Spring-QueryDsl framework - and replace it with a new request with added parameters? That would be a horrible solution, and I feel there has to be a better way to do it via some hook/capability provided by the framework itself.
Unfortunately, there seem to be no comprehensive documentation for Spring QueryDsl support - other than some very simplistic examples.
Thanks for your help!
Answering my own question... I was hoping to find a hook in the framework where I could add the code to enhance the auto-generated predicate with criteria common for all my queries - before it arrives in the controller method, but wasn’t able to figure that out. Overriding QuerydslPredicateArgumentResolver doesn't seem a good or necessary option. And, quite frankly, I've come to the conclusion that this wasn't such a great idea to begin with. It seems that any modifications to the search criteria should be done in a more obvious way - in the business tier. So I decided to simply update the predicate in the service method:
public PageDto<AccountSummaryDto> retrieveByPredicate(Predicate predicate, int pageIndex, int pageSize, String sortBy, String sortOrder) {
Pageable pageRequest = PageRequest.of(pageIndex, pageSize, Sort.Direction.fromString(sortOrder), sortBy);
QAccountSummary accountSummary = QAccountSummary.accountSummary; //QueryDsl auto-generated query type for AccountSummary (path root)
// construct new enhanced search predicate w/added criteria common for all queries
// using original predicate generated by framework from request params as base
BooleanBuilder updatedPredicate = new BooleanBuilder(predicate)
.and(accountSummary.somethingNested.id.eq(SomeThreadContext.getSomethingId()))
.and(accountSummary.deleted.eq(false))
.and(accountSummary.someProperty.eq("xyz"));
Page<accountSummary> page = summarySearchRepository.findAll(updatedPredicate, pageRequest);
return toAccountSummaryPageDto(page); // custom method that converts results to page DTO w/entity dots and page stats
}
The construction of the updated predicate may be extracted into a separate private method on the service should it be desirable to use it in multiple search methods and/or if more logic is required to dynamically generate additional search criteria.
Given the Play Framework 2.3 Computer Database sample application, I would like to practice adding a unique constraint on an attribute. Let's say I want the name attribute of the Computer class to be unique. I've tried to do this by adding a validate() function (and a getter) to Computer.java:
public List<ValidationError> validate() {
List<ValidationError> errors = new ArrayList<ValidationError>();
if(Computer.find.where().eq("name", getName()).findRowCount() != 0){
errors.add(new ValidationError("name", "Name must be unique. That value is already taken."));
}
return errors;
}
public String getName() {
return name;
}
This check works when creating new records in the database, however, this now causes a validation error when you update a Computer object but don't change the name. Is there a way to add a uniqueness constraint, similar to Rails? How can I validate uniqueness in Play?
Thanks!
UPDATE: see the answer by davide.
I ended up using the #Column(unique = true) constraint from the javax.persistence API. This doesn't generate an error in Play forms; instead, it throws a PersistenceException. Therefore I had to add change my controller to achieve the behavior I wanted. Both the create() and update() actions need a try/catch like this:
try {
computerForm.get().save();
} catch (PersistenceException pe) {
flash("error", "Please correct errors below.");
formData.reject("name", "Name conflict. Please choose a different name.");
return badRequest(createForm.render(computerForm));
}
UPDATE 2: each of the answers below is a possible solution
You need to exclude current entity from unique checking, i.e. like that:
if(Computer.find.where().eq("name", getName()).ne("id", getId()).findRowCount() != 0){
errors.add(new ValidationError("name", "Name must be unique."));
}
It will give you SQL query during update:
select count(*) from computer t0 where t0.name = 'Foo' and t0.id <> 123
And this during create:
select count(*) from computer t0 where t0.name = 'Foo' and t0.id is not null
P.S. ne() expression stands for Not Equal To and of course this approach assumes that your name field is Required
Edit: I sent you pull request with working solution, all you need is to add hidden field in your editForm like:
<input name="id" type="hidden" value='#computerForm("id").value'/>
Other thing is that you can simplify your model, i.e. don't need for getters for public fields.
I not sure if this answer your question, because I'm not familiar with Ruby syntax.
To "create a uniqueness constraint in the database" you can use the javax persistence API. Ebean will also recognize this.
To have a plain uniqueness constraint which involves a single field, you can use the #Column annotation:
#Entity
public class Computer extends Model {
...
#Column(unique = true)
public String name;
...
}
If you need some combination of fields to be unique, instead use the
#Table annotation
#Table(
uniqueConstraints=
#UniqueConstraint(columnNames={"name", "brand"})
)
#Entity
public class Computer extends Model {
...
public String name;
public String brand;
...
}
I hope it helps!
I currently have an object like this (simplified):
public class Image {
public int Id { get; set; }
public int ExternalId { get; set; }
}
Now let's say I have this method (mostly pseudo-code):
public void GetImage(int externalId) {
var existingImage = db.Images.FirstOrDefault(i => i.ExternalId == externalId);
if (existingImage != null) {
return existingImage;
}
var newImage = new Image() { ExternalId = externalId };
db.Images.Attach(newImage);
db.SaveChanges();
return newImage;
}
Because ExternalId isn't a key, the change tracker won't care if I have "duplicate" images in the tracker.
So now, let's say this method gets called twice, at the same time via AJAX and Web API (my current scenario). It's async, so there are two threads calling this method now.
If the time between calls is short enough (in my case it is), two rows will be added to the database with the same external ID because neither existing check will return a row. I've greatly simplified this example, since in my real one, there's a timing issue as I fetch the "image" from a service.
How can I prevent this? I need the image to be returned regardless if it's new or updated. I've added a Unique Constraint in the database, so I get an exception, but then on the client, the call fails whereas it should use the existing image instead of throwing an exception.
If I understand EF correctly, I could handle this by making ExternalId a primary key and then use concurrency to handle this, right? Is there any way to avoid changing my current model or is this the only option?
If you already have property defining uniqueness of your entity (ExternalId) you should use it as a key instead of creating another dummy key which does not specify a real uniqueness of your entity. If you don't use ExternalId as a key you must put unique constraint on that column in the database and handle exception in your code to load existing Image from the database.
I've got a model defined like the following...
#MongoEntity
public class Ent extends MongoModel{
public Hashtable<Integer, CustomType> fil;
public int ID;
public Ent(){
fil = new Hashtable<Integer, CustomType>();
}
}
CustomType is a datatype I've created which basically holds a list of items (among other things). At some point in my web application I update the hashtable from a controller and then read back the size of the item I just updated. Like the following...
public static void addToHash(CustomType type, int ID, int key){
//First I add an element to the list I'm storing in custom type.
Ent ent = Ent.find("byID",ID).first();
CustomType element = user.fil.get(key);
if(element == null) element = new CustomType();
element.add(type);
ent.save();
//Next I reset the variables and read back the value I just stored..
ent = null;
ent = User.find("byID",ID).first();
element = ent.fil.get(ID);
System.out.println("SIZE = " + element.size()); //null pointer here
}
As you can see by my above example I add the element, save the model and then attempt to read back what I have just added and it has not been saved. The above model Ent is a minimal version of the entire Model I'm actually using. All other values in the model including List's, String's, Integer's etc. update correctly when they're updated but this Hashtable I'm storing isn't. Why would this be happening and how could I correct it?
You should probably post on the play framework forum for better help..
Alternatives for a mongodb framework are morphia and springdata which have good documentation.
Not sure how Play maps a hash table to a document value, but it seems it cannot update just the hash table using a mongo operator.
You should be able to mark the whole document for update which would work but slower.
We are using Miscrosoft's code first entity framework (4.1) to map to an existing database. We want to be able to change the datatypes and values of some properties that map one to one with a table. For instance, there is a column on the table that determines if a record is current. It is an integer column, and has values 1 or 2. We don't want to change the database as there are many different applications fetching data from that colum, but it would be nice for our code to have the class that maps to that table have a bool property that is IsActive, which returns true if the table column is 1 and false otherwise. Is there a way to configure the EnityFrame work so that we can define this mapping directly without having two properties on the actual class, one for the integer column (mapped to the database) and one boolean property computed from the other? Can I map the boolean property directly to the integer column?
Simple answer is no. EF is totally stupid in this area and it is completely missing simple type mapping.
That means that you cannot change type of scalar properties and your class indeed has to work with that int property using values 1 and 2 to define your IsActive.
The workaround can be:
public class YourClass
{
public int IsActiveValue { get; set; }
[NotMapped]
public bool IsActive
{
get { return IsActiveValue == 2; }
set { IsActiveValue = value ? 2 : 1; }
}
}
This workaround has some disadvantages
You must have two properties and IsActvieValue must be visible to context
You cannot use IsActive in linq-to-entities queries