I'm having some trouble trying to figure out how to set up a class that has an #Embedded field that must be fetched lazily. I tried to annotate the field with #Basic(fetch = FetchType.LAZY), but it causes the persistence API to treat the field as a basic type that implements Serializable, so it maps the field to a BYTEA field in the database (postgresql). I tested it on Derby too, and the same happens.
I also tried to annotate the fields of the #Embeddable class individually with #Basic(fetch = FetchType.LAZY) instead of annotating the #Embedded field of the entity that has it. The generated schema is correct in this case, but the fields are fetched eagerly when I load instances of the entity.
My understanding is that the #Basic annotation is used on basic fields/properties only, so the first case is expected. But why the fields of the #Embeddable class are fetched eagerly even if they are annotated with #Basic(fetch = FetchType.LAZY)? Also, I know that the fetch strategy can be specified by the #Basic and relationship annotations, but is there any other way to specify that fields should be fetched lazily? I'm using EclipseLink 2.6, but let me know if the behaviour is different for other versions of EclipseLink or for another provider.
Directly you cant, because of how #Embedded objects work, but by setting attributes in the object it should work.
#Basic(fetch=FetchType.LAZY)
Remember that lazy should be use only on collections or big objects, and that setting fetch type on lazy is only a clue for provider, it doesn't mean that it will always fetch it lazy rather than eager.
Related
This is my very first question on stackoverflow, so sorry in advance if anything is not as precise as it should be
In my project, I use Hibernate (as ORM framework) with QueryDSL lib, PostgreSQL as a database.
Basically, I need to check the size of a list 'arr', which is a property of some 'X' class, so I googled and found a way to use postgres functions with querydsl as follows (before you ask, I can't use native queries by the requirements):
BooleanBuilder builder = new BooleanBuilder();
builder.and(Expressions.booleanTemplate("function('array_length', {0})", qX.arr)
.castToNum(Integer.class).gt(0));
Everything compiles fine, but when the repository method is being called, I get an error:
ERROR: syntax error at or near "." Position: ...
I checked everything, but there are no "." in that position and near positions as well.
However, after setting spring.jpa.show-sql=true I found out that there is indeed a "." symbol somewhere in that position, and the result SQL statement looks like this:
... and cast(array_length(.) as int4)>?
which means, that JPA can't put my 'arr' inside the array_length() function (is that so?)
Why does this happen? Am I doing something wrong?
Thank you in advance
My entity class looks like that:
#EqualsAndHashCode(callSuper = true)
#Entity
#Table
#Data
#NoArgsConstructor
#TypeDefs({
#TypeDef(name = "list-array", typeClass = ListArrayType.class)
})
public class X extends BaseClass {
// private fields
#Type(type = "list-array")
#Column(name = "arr", columnDefinition = "bigint[]")
#ElementCollection
#OrderColumn
private List<Long> arr;
}
I tried without #ElementCollection and #OrderColumn annotations but that gives me cast errors
#ElementCollection and #OrderColumn are causing a first problem here. After they are removed (and the schema is setup correctly), the function call (SQL template) needs to be corrected.
The problem with #ElementCollection and #OrderColumn is that they represent an alternative approach for storing lists/arrays as part of an entity.
#ElementCollection stores the elements in a separate table, with each element in a separate row (each referencing the entity). To "remember" the correct order, an #OrderColumn is needed as part of the separate table, since rows are returned in arbitrary order if no order is specified (https://stackoverflow.com/a/20050403).
In contrast, ListArrayType and #Column(columnDefinition = "bigint[]") will enable saving the sequence of elements in one column of an entity row. Therefore, no separate table is used, and since the elements are not saved in separate rows, no additional order information is needed.
So without #ElementCollection and #OrderColumn the list mapping is already correctly setup. Be aware that your schema might currently be in a bad state, and you need to make sure that there is a bigint[] column in the entity table (can e.g. be auto-created by hibernate when #ElementCollection and #OrderColumn are removed).
2. Fixing the PostgresQL function call: array_length needs a second argument indicating the dimension of the array along which the length is returned (https://www.postgresql.org/docs/current/functions-array.html). So specifying the template string as follows should get you the correct result:
"function('array_length', {0}, 1)"
("1" being the requested array dimension).
I am trying to come up with a way of implementing tags for my entity that works well for me and need some help in the process. Let me write down some requirements I have in mind:
Firstly, I would like tags to show in entities as a list of strings like this:
{
"tags": ["foo", "bar"]
}
Secondly, I need to be able to retrieve a set of available tags across all entities so that users can easily choose from existing tags.
The 2nd requirement could be achieved by creating a Tag entity with the value of the Tag as the #Id. But that would make the tags property in my entity a relation that requires an extra GET operation to fetch. I could work with a getter method that resolves all the Tags and returns only a list of strings, but I see two disadvantages in that: 1. The representation as a list of strings suggests you could store tags by POSTing them in that way which is not the case. 2. The process of creating an entity requires to create all the Tags via a /tags endpoint first. That seem rather complicated for such a simple thing.
Also, I think I read somewhere that you shouldn't create a repository for an entity that isn't standalone. Would I create a Tag and only a Tag at any point in time? Nope.
I could store the tags as an #ElementCollection in my entity. In this case I don't know how to fulfill the 2nd requirement, though.
#ElementCollection
private Set<String> tags;
I made a simple test via EntityManager but it looks like I cannot query things that are not an #Entity in a result set.
#RestController
#RequestMapping("/tagList")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class TagListController implements RepresentationModelProcessor<RepositoryLinksResource> {
#PersistenceContext
private final #NonNull EntityManager entityManager;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<EntityModel<TagList>> get() {
System.out.println(entityManager.createQuery("SELECT t.tags FROM Training t").getFirstResult());
EntityModel<TagList> model = EntityModel.of(new TagList(Set.of("foo", "bar")));
model.add(linkTo(methodOn(TagListController.class).get()).withSelfRel());
return ResponseEntity.ok(model);
}
}
org.hibernate.QueryException: not an entity
Does anyone know a smart way?
The representation as a list of strings suggests you could store tags by POSTing them in that way which is not the case
This is precisely the issue with using entities as REST resource representations. They work fine until it turns out the internal representation (entity) does not match the external representation (the missing DTO).
However, it would probably make most sense performance-wise to simply use an #ElementCollection like you mentioned, because you then don't have the double join with a join table for the many-to-many association (you could also use a one-to-many association where the parent entity and the tag value are both part of the #Id to avoid a join table, but I'm not sure it's convenient to work with. Probably better to just put a UNIQUE(parent_id, TAG) constraint on the collection table, if you need it). Regarding the not an entity error, you would need to use a native query. Assuming that you have #ElementCollection #CollectionTable(name = "TAGS") #Column(name = "TAG") on tags, then SELECT DISTINCT(TAG) FROM TAGS should do the job.
(as a side note, the DISTINCT part of the query will surely introduce some performance penalty, but I would assume the result of that query is a good candidate for caching)
I have a MongoRepository class
public interface UserRepository extends MongoRepository<User, Long> {
User findById(Long id);
}
and my Entity pojo looks like this
#Document(collection = "user")
class User {
Long id;
String name;
Department department;
…
}
When I call the findBy method, a User object is returned. I want to know how does Spring Data MongoDB converts DBObject to Java object. I was under the impression that Spring Data MongoDB uses some sort of mapper (Jackson?) under the hood which would call setters/constructors method of the java(Entity) class based on the field names in the class or #Field Annotation. But to my surprise, the setters are never invoked. Only the default constructor is invoked.
Then how does the fields are set? The reason I am asking is if the setters are called, it would give me an option to set some other fields may be.
Thanks
Spring Data defaults to field access as accessor methods can contain additional logic that we don't want to trigger by accident. If that's what you actually want though, you can switch to property access by annotating your class with #AccessType(Type.PROPERTY).
Spring has a entity converter at the subsequent layer below it. It uses reflection to read the type of field, variables and signature. The conversion logic is generic for all data repositories. You can read about the same here
You can also introduce a custom converter be it yours or jackson, an example of it is here
Take a look at MappingMongoConverter class - it has the logic which does all this.
Question: Am I somehow able to map two fields of my Entity class to only one Column in the Database?
Scenario: The database is not fully normalized. There exists one Column which contains a composite information. It is not my actual use case, but an comprehensible example might be X- and Y-coordinate of a point in the plane. So the Database may contain a String 12:45 and the Entity class should contain only two integer field x width value 12 and ywith value 45.
Currently the Entity class has just two additional getter and setter for x and y and performs the proper translation. But I am wondering if there is a way to let JPA do this for me magically in the background.
I am already working with custom converter classes, e.g. for a proper mapping between between enums and database columns, but this works only for a "one-to-one" mapping between the field in the Entity class and the column in the database.
Of course it would be the most preferable way to redesign the table in the database, but that's not an option at the moment.
Vendor specific solutions are also fine.
2 Entity fields into one database column can be done fairly simply by specifying JPA use your accessor in the entity to handle the conversion:
#Entity
#Access(AccessType.FIELD)
class myEntity {
#Id
int id;
#Transient
String x;
#Transient
String y;
#Mutable //EclipseLink specific to prevent change tracking issues
#Access(AccessType.PROPERTY)
#Column(name="yourDatabaseFieldName")
private String getCoords() {
return x+":"+y;
}
private void setCoords(String coords) {
//parse the string and set x+y.
}
EclipseLink and Hibernate have transformation mappings that are able to handle the reverse; 2 or more database fields into one java property but this is outside of JPA.
Example use case:
class Address {
#XMLTransient
#ManyToOne(cascade={})
private Person person;
}
In my use case Address is serialized to XML via JAXB, modified in another system, deserialized from XML to a detached JPA entity and then merged back to db (em.merge(address)). As the Person property is marked #XMLTransient it is restored from XML with null.
As all Address' have a database entry and could be identified by there Id I'd like merge to ignore the Person property and just keep the database value for the relation (this has nothing to do with cascade).
Is there a way to tell JPA to ignore the Person property on merge or would I have to use an #XMLAdapter to set the property with the corresponding Person object before merge (btw I also use optimistic locking with #Version on all entities).
Any hints?
Miguel
It sounds like you don't want this relation to be updated by JPA at all, upon a merge. Is that correct? If so, have you tried setting updatable=false on the #ManyToOne?