Filter elements of fetched collection. EclipseLInk. Criteria API - eclipselink

I have an object:
#Entity
#Table(name = "users", catalog = "adm", uniqueConstraints = #UniqueConstraint(columnNames = "loginUser"))
public class User implements GenericEntityPK<Integer> {
private Set<Permission> permissions = new HashSet<Permission>(0);
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "user_l_permission", catalog = "adm", joinColumns = {
#JoinColumn(name = "idUser", nullable = false, updatable = false) }, inverseJoinColumns = {
#JoinColumn(name = "idPermission", nullable = false, updatable = false) })
public Set<Permission> getPermissions() {
return this.permissions;
}
}
I want to select user entity and fetch collection of permissions, with where clause. In other words permissions collection must contain only elements that satisfy where conditions.
I've tried to do it with SetJoin but with no success..
Is there a way to do it?

I would recommend you instead query for the permissions that match the user and filter you want when needed. You can even cache this collection in your entity with a transient collection to pass it around in the object. Using filters on relationships ends up causing problems and corrupts the cache as what is in the object is no longer a true representation of what is in the database.
That said, there is no JPA way to do it, but Eclipselink supports additional criteria

Related

How to access the join column (Foreign key) in Spring Data JPA without loading the mapped entity

I have a POST entiry and HeadingSlugEntity.
public class PostEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String postId;
private String uid;
private String dateCreated;
private String dateUpdated;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "headingSlugId", referencedColumnName = "headingSlugId")
private HeadingSlugEntity headingSlugEntity;
}
I have a 1:1 mapping for HeadingSlugEntity. When I save post entity and headingSlugEntity together, headingSlugId is getting auto-populated. That is working fine.
The next time when I fetch the post entity, I need to access the headingSlugId without getting the headingSlugEntity. (At this point of time, I don't need HeadingSlugEntity, I just need its ID.) I just need to get the headingSlugId. How to do it.
Since this entity is not having the field headingSlugId, I can't set and get it. If I add a field named headingSlugId inside post entity, I will get hibernate duplicate field error. If I put
insertable = false, updatable = false)
and add field headingSlugId, the headingSlugId will be Null.
What basic stuff I am missing here ?
You can map the foreign key to the entity twice in this case, one for actual ORM and another for getting the FK without actually firing a new query.
public class Answer {
#Column(name = "headingSlugId", insertable = false, updatable = false)
private Integer headingSlugId;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "headingSlugId", referencedColumnName = "headingSlugId")
private HeadingSlugEntity headingSlugEntity;
}

Cant get hibernate to update object without EntityExistException being thrown

I am trying to do a bidirectional one to one relation and when updating the AccountExtrasModel on the first save it works fine but when updating I get either an error or the sql statements adds an insert and then a delete instead of an update.
import lombok.*;
import javax.persistence.*;
#ToString
#NoArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "Account")
public class AccountModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Account_ID;
#Column(nullable = false, updatable = false)
private String name;
#Column(nullable = false, unique = true, updatable = false)
private String email;
#Column(nullable = false, updatable = false)
private String password;
#OneToOne(mappedBy = "accountModel", cascade = CascadeType.ALL, orphanRemoval = true)
private AccountExtrasModel accountExtras;
public AccountModel addExtras(AccountExtrasModel accountExtrasModel) {
accountExtrasModel.setAccountModel(this);
this.setAccountExtras(accountExtrasModel);
return this;
}
}
import lombok.*;
import javax.persistence.*;
#Setter
#Getter
#NoArgsConstructor
#ToString
#Entity
#Table(name = "AccountExtras")
public class AccountExtrasModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long ID;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
private AccountModel accountModel;
#Lob
private String description;
private String[] myVideos;
private String[] likedVideos;
private String imageReference;
}
If i change the #MapsId to #JoinColumn in AccountExtrasModel then i get the desired result but what its doing is inserting a new row and linking it to acccount and then deleting the old row instead of doing an update.
This is the error im getting:
{
"timestamp": "2018-04-26T18:19:01.657+0000",
"status": 500,
"error": "Internal Server Error",
"message": "A different object with the same identifier value was already associated with the session : [com.alttube.account.models.AccountExtrasModel#1]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.alttube.account.models.AccountExtrasModel#1]",
"path": "/update_account"
}
What can i do to get the desired result which is to simply perform an update on the accountExtrasModel to the corresponding account to which it belongs?
If you update an AccountModel instance by setting a new instance of AccountExtrasModel, as you do in addExtras(), then it is normal to have a delete+insert. By updating the id you would end with orphaned records.
When you set new values on AccountExtrasModel instance, check if accountExtras is initialized. If not, do the addExtras() stuff. If it is, do not replace it, just change it, so hibernate will generate an update on the extras table (but keep the record id unchanged).
Instead of use the method save try use the method merge, because in case of object exist in database the framework will update this object.
Case the object don't exist, the framework will insert as normal.
Regards!

How to init array property of annotation in Kotlin

#Entity
#Table(name = "t_payment")
data class PaymentEntity(
#ManyToOne(cascade = CascadeType.DETACH)
#JoinColumn(name = "user_id", nullable = false)
var user: User? = null) : Serializable {
}
as a result of a compilation error: "Type mismatch"
cascade annotation attribute accepts an array of CascadeType thus you need to change your #ManyToOne usage to:
#ManyToOne(cascade = arrayOf(javax.persistence.CascadeType.DETACH))

JPA #ElementCollection generates strange unique key

I have an entity class PositionOrdering which contains an element collection:
#ElementCollection(targetClass = Position.class, fetch = FetchType.EAGER)
#CollectionTable(name = "POSITION_ORDERING_POSITION",
joinColumns = #JoinColumn(name = "position_ordering_id"))
#OrderColumn
List<Position> positions = new ArrayList<>();
When hibernate generates the database structure, it looks like this:
CREATE TABLE wls.position_ordering_position
(
position_ordering_id bigint NOT NULL,
positions_id bigint NOT NULL,
positions_order integer NOT NULL,
...
}
It's ok and exactly what I was expected. But it also generate a unique contsraint on positions_id column. It is strange, because the position id should be unique only per ordering, so any of the following unique keys would be ok:
position_ordering_id + positions_order
position_ordering_id + positions_id
But not on the single column of positions_id.
Because the constraint is generated automatically, I can't ignore or remove it simply.
Can I configure my collection to create correct unique constraint or at least not to create any?
UPDATE:
As for request, here is the skeleton of the Position entity:
#Entity
#SequenceGenerator(name = EntityBase.SEQUENCE_NAME,
sequenceName = "POSITION_ID_SEQ")
#Table(name = "position")
public class Position extends EntityBase {
// Lots of fields, like row, column number, and type, etc.
}
Where EntityBase is a simple class with some utility function and with Id:
#MappedSuperclass
public abstract class EntityBase implements Serializable, Cloneable {
public static final String SEQUENCE_NAME = "SEQUENCE_GENERATOR";
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = SEQUENCE_NAME)
protected Long id;
//..
}
#ElementCollection is used for mapping basic types or #Embedded classes, not entities. From the documentation
An ElementCollection can be used to define a one-to-many relationship to an Embeddable object, or a Basic value (such as a collection of Strings).
Since Position is an #Entity, you should map it as #OneToMany or #ManyToMany. I don't know the exact reason why are you getting that unique key generated, but I guess you can expect unpredictable results if you use the annottion in a was that it was not intended for.
As Predrag Maric described it in the accepted answer, the problem was that Position was not an `Embeddable'. My solution was:
I created a support class which wraps the Position into an #Embeddable entity:
#Embeddable
//#Table(name = "position_ordering_position")
public class PositionOrderingPosition {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "position_id", nullable = false)
private Position position;
public PositionOrderingPosition() {
}
public PositionOrderingPosition(Position position) {
this.position = position;
}
public Position getPosition() {
return position;
}
}
Also I changed the Element collection to this:
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "POSITION_ORDERING_POSITION",
joinColumns = #JoinColumn(name = "position_ordering_id"))
#OrderColumn
List<PositionOrderingPosition> positions = new ArrayList<>();
Now it creates the same table, but with the right constraints.

How are association tables/foreign keys best handled with hibernate?

I used schemaExport to create my tables like this
Configuration config = new Configuration();
config.addAnnotatedClass(Categories.class);
config.addAnnotatedClass(Article.class);
config.configure();
new SchemaExport(config).create(true, true);
The important pieces of Categories.java and Article.java are these:
Article.java
#Id
#GeneratedValue
public Long getId() {
return id;
}
#OneToMany( cascade = CascadeType.ALL )
public Set<Categories> getCategories() {
return categories;
}
Categories.java
#Id
#GeneratedValue
public Long getId() {
return id;
}
#ManyToOne(cascade = CascadeType.ALL)
public Article getArticleCategories() {
return articleCategories;
}
When run, Schema Export generates three tables: Article, Categories and article_categories. article_categories is an association table with two primary keys matching the keys from Article and Categories. Only Article and Categories have been added to the SessionFactory in my ApplicationContext.xml because I obviously don't have the third table as a class since it was generated. I wrote this HQL:
"from Article a, article_categories ac, Categories c where ac.Article_id = a.id and ac.categories_id = c.id and c.category = 'p'"
Hibernate can't run it because article_categories is not mapped. Do I need to map it? Is running native SQL instead of HQL a possible alternative, or should I avoid association tables all together? What's the solution to this issue?
HQL doesn't work with tables. It works with entities and their associations. Create joins between the entities in the HQL query, and Hibernate will generate the appropriate SQL, using the join tables, for you.
Example:
select a from Article a
inner join a.categories c
where c.category = :categoryName
Read (at least) the HQL section of the reference manual.
Side note: the name of the entities should be singular: Category and not Categories. An article thus has a Set<Category>, named categories. Much easier to read and understand that way. And the category field of Category should be named name: it's the name of the category, and not its category.
Not too sure, if you have an error in your original database or the export. We use the same approach but refactor the tables afterwards. (I'll try to find the script we use, if you need it and get back to this tomorrow)
In any case: You don't need an entity class for your m:n relationship, if there ar no additional fields other than the foreign keys:
In Category:
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "article_categories",
joinColumns = { #JoinColumn(name = "article_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "category_id", nullable = false, updatable = false) })
private Set<Article> articles;
In Article:
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "article_categories",
joinColumns = { #JoinColumn(name = "category_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "article_id", nullable = false, updatable = false) })
private Set<Category> categories;
This will create the m:n table.
When we run the hibernate exporter, we actually get a mapping class and a key class, which you'll need if you want additional columns in the m:n table. But like I said, I'll have to dig up the snippet tomorrow.