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

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;
}

Related

Spring data jpa native query. Insert object

I'm using a native query on my repository. I want to insert object data, but the query doesn't understand the object fields. How to solve this problem, I don't want to have a method with a lot of parameters.
My class:
public class StudentDTO {
private long numberzachetka;
private String fio;
private Date entrydate;
private int course;
private int numbergroup;
private long specialty;
private long faculty;
//getters, setters..}
Native query:
#Query(value ="insert into student(numberzachetka,fiostudent,entrydate,course,numbergroup,specialtykey,facultynumber)" +
" values" +
" (:student.numberzachetka,:student.fio,:student.entrydate,:student.course,:student.numbergroup,:student.specialty,:student.faculty)", nativeQuery = true)
void addNewStudent(#Param("student") StudentDTO studentDTO);
Student entity:
#Entity
#Table(name="student")
public class Student {
#Id
#Column(name="numberzachetka", nullable = false)
private long numberzachetka;
#Column(name="fiostudent", nullable = false, length = 100)
private String fio;
#Temporal(TemporalType.DATE)
#Column(name = "entrydate", nullable = false)
private Date entrydate;
#Column(name="course", nullable = false)
private int course;
#Column(name="numbergroup", nullable = false)
private int numbergroup;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "specialtykey", nullable = false)
private Specialty specialty;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "facultynumber", nullable = false)
private Faculty faculty;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "student")
private Set<Performance> performances;
#Query(value ="insert into student(numberzachetka,fiostudent,entrydate,course,numbergroup,specialtykey,facultynumber)" +
" values" +
" (:student.numberzachetka,:student.fio,:student.entrydate,:student.course,:student.numbergroup,:student.specialty,:student.faculty)", nativeQuery = true)
void addNewStudent(#Param("student") StudentDTO studentDTO);
This is not the right way to save value in db using JPA repository.
If you want to save value in DB there is very simpler way, follow below steps:
Copy all the values from the studentDTO to Student object using below code.
Student student = new Student();
BeanUtils.copyProperties(studentDto,student);
create StudentRepository interface
#Repository public interface StudentRepository extends
JpaRepository<Student, Long> { }
#Autowired above Repository in the service class
#Autowired private StudentRepository studentRepository;
Save the Student object using repository predefined save method.
studentRepository.save(student);
These are the minimal change you want to save object in db, instead of going through native SQL query.
Within the entity you could have a constructor accepting the DTO. The constructor transfers the DTO attributes to the fields of the entity. Then you can just use the JpaRepository save method. You don't need the select statement. Keep in mind to add also an empty constructor which is required by JPA.
Another Idea which I use in my projects is to use mapstruct to transfert attributes from DTOs to entities.
The way you present your idea is not possible. JPA does not know how to map a DTO to an entity.

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!

Filter elements of fetched collection. EclipseLInk. Criteria API

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

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.

JPA COUNT with composite primary key query not working

In my db, I have a table (Defaults), and when I generate an entity from table, I get these two classes:
#Entity
public class Defaults implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected DefaultsPK DefaultsPK;
#Column(name = "ERTEK")
private String ertek;
getter/setter...
}
#Embeddable
public class DefaultsPK implements Serializable {
#Basic(optional = false)
#Column(name = "VALUE_1")
private String value1;
#Basic(optional = false)
#Column(name = "TYPE")
private String type;
#Basic(optional = false)
#Column(name = "VALID_FROM")
#Temporal(TemporalType.TIMESTAMP)
private Date validFrom;
#Basic(optional = false)
#Column(name = "VALID_TO")
#Temporal(TemporalType.TIMESTAMP)
private Date validTo;
getter/setter...
}
That is why becaues the primary key is including the values.
I want to count all the rows in the table, so I use this code:
String sql = "SELECT COUNT(d) FROM Defaults d";
Query q = em.createQuery(sql);
long count = (long)q.getSingleResult();
But I am getting this error:
org.hibernate.exception.SQLGrammarException: could not execute query
...
java.sql.SQLSyntaxErrorException: ORA-00907: The right expression is missing from the arithmetic expression
What is the problem? The other count queries with other entities are working.
I am using hibernate.
Use count(d.ertek) or count(d.id) instead of count(d). This can be happen when you have composite primary key at your entity.