Hibernate Jpa custom query - java

I'm facing a problem that makes me sweat.
first pojo:
#Entity
#Table(name = "generic_params")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", defaultImpl = Void.class)
public class GenericParams extends AbstractParam {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "code", unique = true)
private String code;
#Column(name = "type", insertable = false, updatable = false, unique = true)
private String type;
#JsonManagedReference
#OneToMany(mappedBy = "genericParams", cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
private Set<LabelTranslation> labelTranslations = new HashSet<>();
...getter / setter
second pojo:
#Entity
#Table(name = "label_translation")
public class LabelTranslation extends AbstractParam {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "label")
private String label;
#ManyToOne
private Language language;
#Column(name = "language_id", insertable = false, updatable = false)
private Long languageId;
#ManyToOne(fetch = FetchType.LAZY)
#JsonBackReference
private GenericParams genericParams;
#Column(name = "generic_params_id", insertable = false, updatable = false)
private Long genericParamsId;
GenericParamsMapper
#Mapper(componentModel = "spring", uses = { LabelTranslationMapper.class })
public interface GenericParamsMapper extends GenericParamsParameterizedMapper<GenericParams> {
#Override
GenericParamsDTO toDto(GenericParams genericParams);
#Override
GenericParams toEntity(GenericParamsDTO genericParamsDTO);
#Override
GenericParams fromSuperClassToDerived(GenericParams genericParams);
}
The problem when I make the request to return a type of GenericParams it returns all LabelTranslation without filter.
My request:
#Query(value = " SELECT gparam FROM GenericParams gparam " //
+ " INNER JOIN gparam.labelTranslations label with label.languageId =:lang " //
+ " WHERE gparam.type =:type " //
+ " AND gparam.deleted = false ") //
List<GenericParams> getGenericParamByFilteringLabelTranslation(#Param("lang") Long lg, #Param("type") String tp);
this request is called by :
#Override
public List<GenericParamsDTO> getGenericParamsByType(String type, HttpServletRequest request) {
Long idLang = this.languageService.findIdByCulture(request.getLocale().toLanguageTag());
List<GenericParams> gList = this.genericParamsRepository.getGenericParamByFilteringLabelTranslation(idLang, type);
result this.genericParamsMapper.toDto(gList);
}
Here it is supposed to retrieve a list of GenericParams with the filter on LabelTranslation which have the 2L language for example
except that here it returns all LabelTranslation without filter.
You'll say yes because the mapper ignores the property.
I will tell you that I need to ignore it for other need.
So how do i ignore it for one need and filter it for another.
Getter and setter are omitted in order to simplify
thanks for lending me a hand.
it was more challenging than I thought
by analyzing the queries executed by hibernate, I realized the error.
despite that i don't know how to solve it.
This first request is good:
select gp.* from generic_params gp
inner join label_translation labeltrans1_
on gp.id=labeltrans1_.generic_params_id and (labeltrans1_.language_id=? and labeltrans1_.language_id=?)
where gp.type=?
and gp.is_deleted=0;
but this returns the false information (since it is executed automatically by Hibernate)
so it's a design problem
select * from label_translation lb
left outer join lang lang on lb.language_id=lang.id
where lb.generic_params_id=?
now how do i tell hibernate to execute a request like this?
select label.* from label_translation label
left outer join lang langue on label.language_id=langue.id
where label.generic_params_id=200
and langue.id=2;
you will notice that I just added this piece of code:
and langue.id=2;
So to make a summary, I left things as they are and I added in the mapper a method that filters on LabelTransaltion and I have the right result.
a second option is to make two requests:
- The first one returns the GenericParams
- The second one returns the LabelTransaltion and then in the GenericParams object i set the list of LabelTransaltion by the 2nd request.
But I wonder if there is another more concise hibernate way.
thank you

Related

Circumventing Attribute Limitations of Many-to-Many Relations in JPA

I am using PostgreSQL 12.11, JPA 3.1.0, and Hibernate 5.6.10. This might become important because I am doing things that apparently do not work with JPA 2.0.
My goal is to add an attribute to a many-to-many relationship. I found this posting. #Mikko Maunu states that "There is no concept of having additional persistent attribute in relation in JPA (2.0)." To me, this sounds like what I want to do is not possible. However, the answer is rather old and might not be complete anymore.
Beside the time gap and the version gap, this is, in my opinion, a new question because I am doing something that is probably questionable and not part of the original thread.
What I did is this:
Create a #ManyToMany relationship in JPA and specify a #JoinTable.
Manually define an entity with identical table name to the table specified in 1. For this table, I chose a composite primary key using #IdClass. I also added my attribute.
Inside one of the n:m-connected entities, create a #OneToMany relationship to the connection-table-entity created in 2. However, I did not create a corresponding #ManyToOne relationship as that would have created an error.
As a result, I can access the original entities and their relation as many-to-many, but also the relation itself, which is not an entity in the original ERM, but it is for JPA. First tests show this seems to be working.
I am aware, however, that I basically access the same part of the persistence (the PostgreSQL database) through two different ways at the same time.
Now my questions are:
Is this a valid way to do it? Or will I get in bad trouble at one point?
Is there a situation where I will need to refresh to prevent trouble?
Is this something new in JPA > 2.0, or just an extension to the original answer?
This should help.
Here is how I do it:
#Entity
#Table(name = "person", schema = "crm")
public final class Person implements Serializable {
#Id
#Column(name = "id", unique = true, nullable = false, updatable = false, columnDefinition = "bigserial")
private Long id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "person", orphanRemoval = true)
private Set<PersonEmail> emails = new HashSet<>();
}
#Entity
#Table(name = "email", schema = "crm")
public final class Email implements Serializable {
#Id
#Column(name = "id", unique = true, nullable = false, updatable = false, columnDefinition = "bigserial")
private Long id;
#Column(name = "email", nullable = false, length = 64, columnDefinition = "varchar(64)")
private String localPart;
#Column(name = "domain", nullable = false, length = 255, columnDefinition = "varchar(255)")
private String domain;
}
#Entity
#Table(name = "person_email", schema = "crm")
public final class PersonEmail implements Serializable {
#EmbeddedId
private PersonEmailId id;
// The mapped objects are fetched lazily.
// This is a choice.
#ToString.Exclude
#MapsId("personId")
#ManyToOne(fetch = FetchType.LAZY, optional = false)
private Person person;
#ToString.Exclude
#MapsId("emailId")
#ManyToOne(fetch = FetchType.LAZY, optional = false)
private Email email;
// Here's an extra column.
#Column(name = "type", nullable = false, columnDefinition = "email_type_t")
#Convert(converter = EmailType.EmailTypeConverter.class)
private EmailType type;
public final void setPerson(final Person person) {
this.person = person;
id.setPersonId(this.person.getId());
}
public final void setEmail(final Email email) {
this.email = email;
id.setEmailId(this.email.getId());
}
#Embeddable
public static final class PersonEmailId implements Serializable {
#Column(name = "person_id", nullable = false, insertable = false, updatable = false, columnDefinition = "bigint")
private Long personId;
#Column(name = "email_id", nullable = false, insertable = false, updatable = false, columnDefinition = "bigint")
private Long emailId;
}

Why does the order of #JoinColumns matter?

I have discovered some strange behavior with Hibernate. I have a One to Many relationship between two entities that use embedded composite primary keys like this. (and yes, I know the data design is awful, but this is the schema I have to work with)
#Entity
#Table(name = "T_PLACE")
public class Place {
#EmbeddedId
private PlacePK id;
#OneToMany(mappedBy = "place")
private List<Mode> modes;
// getters and setters
}
#Embeddable
public class PlacePK implements Serializable {
#Column(name = "COMPANY")
private String company;
#Column(name = "AREA")
private String area;
#Column(name = "COLOR")
private String color;
// getters and setters
}
#Entity
#Table(name = "T_MODE")
public class Mode {
#EmbeddedId
private ModePK id;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "COMPANY", insertable = false, updatable = false),
#JoinColumn(name = "AREA", insertable = false, updatable = false),
#JoinColumn(name = "COLOR", insertable = false, updatable = false),
})
private Place place;
private String function;
// getters and setters
}
#Embeddable
public class ModePK implements Serializable {
#Column(name = "COMPANY")
private String company;
#Column(name = "AREA")
private String area;
#Column(name = "COLOR")
private String color;
#Column(name = "MODE_ID")
private String color;
// getters and setters
}
But the resulting HQL ends up ordering it like this when querying for a place's modes
where
company=?
and color=?
and area=?
and it ends up binding area to the second ? and color to the third ?.
It doesn't work unless I change the order of the #JoinColumns to put color before area.
#JoinColumns({
#JoinColumn(name = "COMPANY", insertable = false, updatable = false),
#JoinColumn(name = "COLOR", insertable = false, updatable = false),
#JoinColumn(name = "AREA", insertable = false, updatable = false),
})
So my question is, what is causing this behavior? What determines the order of the where clause in the HQL? This isn't any issue because I've figured out how to make it work, but I'd like to understand it.
I am using spring-boot-starter-data-jpa:1.5.10-RELEASE which uses Hibernate 5.
Edit
Here is how I'm producing the HQL
#Repository
public interface PlaceRepository extends JpaRepository<Place, PlacePK> {}
and then in a test:
PlacePK placePK = new PlacePK();
placePK.setCompany("Acme");
placePK.setArea("XYZ");
place.PK.setColor("Blue");
Place place = placeRepository.findOne(placePK);
List<Mode> modes = place.getModes(); // ends up being an empty PersistBag until I switch the order of the #JoinColumns
assertNotNull(modes);
The order mattes because based on this order Hibernate builds a join condition. Hibernate doesn't know how to map specific columns to each other (even though, it could do it through naming comparison, but...). So it does it by simply putting them in the same order as you specified versus the ID columns.
To see what difference ordering does, switch on logging on the produced SQL queries. You will see that if you order is not aligned with the order of the ID keys, your fetch query may end up in something like
...join PlacePK pk ON pk.COMPANY = m.AREA*emphasized text*

Hibernate Lazy Loading makes criteria slow to run

I am experiencing a problem with hibernate and lazy loading of objects.
basically I want to load an class which has an eagerly loaded field and not load the lazy fields of child classes
Take the following QuestionVO class
#Entity
#Table(name = "question")
public class QuestionVO extends BaseDAOVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = -5867047752936216092L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#Column(name = "questionText", unique = false, nullable = false, length = 4000)
#Size(min = 3, max = 4000)
#Pattern(regexp = MobileAppsRegexConstants.GENERAL_ALLOWED_CHARCHTERS, message = "Question Text Not valid.")
private String questionText;
#ManyToOne(fetch = FetchType.EAGER)
#Cascade({ CascadeType.SAVE_UPDATE })
#JoinColumn(name = "MENU_STYLE_ID", nullable = true)
private MenuStyleVO menuStyle;
}
Take the following MenuStyleVO class
#Entity
#Table(name = "menu_style")
public class MenuStyleVO extends BaseDAOVO implements Serializable{
/**
*
*/
private static final long serialVersionUID = 3697798179195096156L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#Column(name = "menuStyleName", unique = false, nullable = false, length = 200)
private String menuStyleName;
#Column(name = "menuTemplate", unique = false, nullable = false, length = 200)
private String menuTemplate;
#OneToOne(fetch = FetchType.LAZY, optional=false)
#Cascade({ CascadeType.SAVE_UPDATE })
#JoinColumn(name="logo_id")
#JsonProperty("logo")
private ApplicationImageVO logo;
}
And this ApplicationImageVO class
#Entity
#Table(name = "application_image")
public class ApplicationImageVO extends BaseDAOVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = -9158898930601867545L;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "image1242x2208")
#Cascade({ CascadeType.ALL })
#JsonIgnore
private SubmissionLauncherImagesVO launcherImage1242x2208;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "image1536x2048")
#Cascade({ CascadeType.ALL })
#JsonIgnore
private SubmissionLauncherImagesVO launcherImage1536x2048;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "image2048x1536")
#Cascade({ CascadeType.ALL })
#JsonIgnore
private SubmissionLauncherImagesVO launcherImage2048x1536;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "logo")
#Cascade({ CascadeType.ALL })
#JsonIgnore
private MenuStyleVO menuStyleLogo;
}
If L load the QuestionVO class from the database using the following hibernate criteria code - all the lazy fields of MenuStyleVO and ApplicationImageVO are also loaded.
On complicated use cases, this results in this query getting very slow
public QuestionVO findMasterAppQuestionById(int id) {
Criteria criteria = currentSession().createCriteria(QuestionVO.class);
criteria.add(Restrictions.eq("id", id));
QuestionVO questionVO = (QuestionVO) criteria.uniqueResult();
return questionVO;
}
What I am wondering is - would it be possible to load the QuestionVO class and its eager fields and tell hibernate to ignore lazy fields from the other classes bar those that are needed?
Cheers
Damien
Last time we faced an issue like this we used a constructor on parent class, which use only the desired fields of determined query.
I can't remember in fully how constructor inside a jpql query works, but it must be something like this:
select new com.package.class(c.field1, c.field2) from com.package.class c
Remember, a constructor with same arguments must be present on the desired entity.
Pros:
- Better query perfomance;
- Can be replicated with other arguments;
Cons:
- Pretty limited, you can only use this hack on the main entity you are querying;
- Includes a constructor only for determined query, poor design;
Also, you should take a look on EnttyGraphs of JPA. Seems quite promising, but didn't work as desired in our project.
Btw, Hibernate has put us many times on performance issues, hope this hack help you, good luck!
Edit:
Why this pattern would help in performance issues?
Basically, with the example i've showed before, you are not loading everything via Hibernate, only the two fields (field1 and field2) of the main entity. Without using a constructor you shoudn't be able to do that, because your query would not result in a collection of the desired entity, but in a collection of two objects each iteration (Object[]). Using the constructor pattern you are creating instances of the desired entity, but only selecting a few fields from database, and that's why this pattern can help you, you are returning a collection of the desired entity with only a few fields.

JPA Entity field reference OneToOne recursive

I am getting this error when I will persist() my entity. I think that the cause of the error is the relation, my idea is that FolderEntity (represents a virtual folder) can be stay inside another (only one) Then I created the reference to self (In the extended class, because all resources can be inside a folder, and folder is an resource)
org.hibernate.AnnotationException: Referenced property not a (One|Many)ToOne: com.editor.entity.FolderEntity.id in mappedBy of com.editor.entity.FolderEntity.folderId
This my main Entity:
#MappedSuperclass
public abstract class Entity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", nullable = false)
private Integer id;
/** getter/setter **/
}
Then I extends it in my ResourceEntity Entity:
#MappedSuperclass
public class ResourceEntity extends Entity {
#Column(name = "NAME", length = Lengths.NAME40, unique = true, nullable = false)
private String name;
#Column(name = "DESCRIPTION", length = Lengths.DESCRIPTION1000, unique = false, nullable = true)
private String description;
#JoinColumn(name = "FOLDER_ID", updatable = true, nullable = false)
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "id")
private FolderEntity folderId;
/** getter/setter **/
}
Finally, I am working with this entity:
#javax.persistence.Entity
#Table(name = "EDITOR_FOLDERS")
#NamedQueries({
#NamedQuery(name = FolderEntity.ALL_FOLDERS, query = "select f from FolderEntity f"),
#NamedQuery(name = FolderEntity.FOLDER_BY_NAME, query = "select f from FolderEntity f where name = :name and resourceType = :resourceType") })
public class FolderEntity extends ResourceEntity {
public static final String ALL_FOLDERS = "findAllFolders";
public static final String FOLDER_BY_NAME = "findAllFoldersByName";
#Column(name = "RESOURCE_TYPE", length = Lengths.CODE, unique = false, nullable = false)
private Integer resourceType;
/** getter/setter **/
}
Anybodys help me to solve this? Thanks!
You should check the meaning of mappedBy: It does not reference the field that contains the ID (JPA is clever enough to find that one by itself), but it references another XToOne field that "owns" the mapping
public abstract String mappedBy
(Optional) The field that owns the relationship. This element is only specified on the inverse (non-owning) side of the association.
(from javadoc of OneToOne)
In your case you don't need the mappedBy as you are on the owning side. And you should name the attribute folder as you are referencing no ID but an entity.
Another remark: Use an enum for resourceType if you intend to define the possible values in your application as constants.

Implementing pagination in MVC - Servlets + JSP

[MVC, Servlets + JSP, JPA, MySQL]
I am working on simple Blog application. I am using JPA to map entities to MySQL tables. Here is code excerpt from entities in question:
Entity Post:
#NamedQueries({
#NamedQuery(name = "getNewestPosts", query = "SELECT p FROM Post p ORDER BY p.date DESC"), // getting resultList ordered by date
#NamedQuery(name = "getMostVisitedPosts", query = "SELECT p FROM Post p ORDER BY p.visitors DESC") // ordered by most visited
})
#Entity
#Table(name = "post")
public class Post implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "post_id", unique = true, nullable = false)
private Integer id;
#Column(name = "post_title", length=300, unique = false, nullable = false)
private String title;
#Column(name = "post_date", unique = false, nullable = false)
private Date date;
#Column(name = "post_summary", length=1000, unique = false, nullable = true)
private String summary;
#Column(name = "post_content", length=50000, unique = false, nullable = false)
private String content;
#Column(name = "post_visitors", unique = false, nullable = false)
private Integer visitors;
#OneToMany(cascade = { ALL }, fetch = LAZY, mappedBy = "post")
private Set<Comment> comments = new HashSet<Comment>();
...
Entity Comment:
#Entity
#Table(name = "comment")
public class Comment implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "comment_id", unique = true, nullable = false)
private Integer id;
#Column(name = "comment_title", length=300, unique = false, nullable = false)
private String title;
#Column(name = "comment_date", unique = false, nullable = false)
private Date date;
#Column(name = "comment_content", length=600, unique = false, nullable = false)
private String content;
#ManyToOne
#JoinColumn (name = "post_id", referencedColumnName="post_id", nullable = false)
private Post post; ...
Blog home page should contain summaries of 10 newest posts. So, in PostDAO object I have defined next method (returns all posts from db ordered by date):
public List<Post> getNewestPosts(){
Query q = em.createNamedQuery("getNewestPosts");
List<Post> resultList = (List<Post>) q.getResultList();
if (resultList.isEmpty())
return null;
else
return resultList;
}
I would like to implement pagination in some simple way, probably passing certain request parameters and reading data in jsp using jstl (i'm not yet familiar with jquery). Now, how to approach to implementing pagination in MVC? Which parameters I need to be attaching to request? How should I approach to implementing page navigation links (previous, page numbers, next) in JSP?
I think you can use the setMaxResults and the setFirstResult methods on the namedQuery. Keep passing the First result as a function of the number of records to be displayed on the page and the page number.
If you use Spring MVC there is already a way to do it and you can take a look at the PageListHolder api documentation. I havent used this but i have stumbled upon the API.

Categories