JPA : #SequenceGenerator is not generating the sequence - jpa

I my application I have used JPA with Hibernate vendor and Oracle 11G DB.
Here I am using the native query as follow on my MST_EMP table ..
Query query = this.entityManager.createNativeQuery("INSERT INTO MST_EMP emp (" +
"EMP_NAME,EMP_MAIL_ID) VALUES ('dasdas',?)");
query.setParameter(1,"dhrumil");
query.executeUpdate();
Here is my MST_EMP entity details..
#Table(name = "MST_EMP")
public class MstEmp implements Serializable, IsEntity {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "EMP_CODE")
#SequenceGenerator( name = "EMP_CODE_SEQ", sequenceName = "EMP_CODE_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "EMP_CODE_SEQ")
private String empCode;
#Column(name="EMP_MAIL_ID")
private String empMailId;
#Column(name="EMP_NAME")
private String empName;
public MstEmp() {
}
public String getEmpCode() {
return this.empCode;
}
public void setEmpCode(String empCode) {
this.empCode = empCode;
}
public String getCreatedBy() {
return this.createdBy;
}
public void setEmpMailId(String empMailId) {
this.empMailId = empMailId;
}
public String getEmpName() {
return this.empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
}
As per my understanding, we dont need to give value to EMP_CODE in the native query. Because sequence is associated with it.
But this query gives me error like this ..
SEVERE: ORA-01400: cannot insert NULL into ("PERK"."MST_EMP"."EMP_CODE")
SEVERE: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute native bulk manipulation query
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1179)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1112)
Can any one tell me, Do we need to provide EMP_CODE in the native query ?
Will native query , do not refer the sequence automatically declared in the Entity?
Thanks.

JPA only generates the sequence automatically when you persist a new object via EntityManager.persist() method:
E.g.
EntityManager em = \\ ... Initialise
MstEmp newMstEmp = new MstEmp();
newMstEmp.setEmpCode(...);
newMstEmp.setEmpMailId(...);
newMstEmp.setEmpName(...);
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(newMstEmp);
tx.commit();
When you apply JPQL directly to the DB (via entityManager.createQuery()) or raw SQL directly to the DB (via entityManager.createNativeQuery() - as you do here), you must insert your own sequence:
Query query = this.entityManager.createNativeQuery("INSERT INTO MST_EMP emp (" +
"EMP_CODE,EMP_NAME,EMP_MAIL_ID) VALUES (EMP_CODE_SEQ.nextval,'dasdas',?)");
query.setParameter(1,"dhrumil");
query.executeUpdate();

Related

Problem when testing via #DataJpaTest with composite key entities

I'm trying to test an entity that I've mapped with a composite key.
Simply, I tried to test from the Repository layer, but the desired data and results did not come out.
I don't use #CreationTimestamp very often, so you don't have to mention this part, but if this part is a problem,
please mention it.
Please take into account typos. Thanks.
The code looks like this:
#Entity
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#EqualsAndHashCode
#Getter
#IdClass(TestId.class)
#ToString
public class TestEntity {
#Id
private Long groupCode;
#Id
private Long code;
#CreationTimestamp
#Column(nullable = false, updatable = false)
private LocalDateTime regDate;
#UpdateTimestamp
#Column(insertable = false)
private LocalDateTime modDate;
public TestEntity(Long groupCode, Long code) {
this.groupCode = groupCode;
this.code = code;
}
}
Repository
public interface TestEnttiyRepository extends JpaRepository<TestEntity, TestId> {
}
Composite-id Class
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#EqualsAndHashCode
public class TestId implements Serializable {
private Long groupCode;
private Long code;
public TestId(Long groupCode, Long code) {
this.groupCode = groupCode;
this.code = code;
}
}
#DataJpaTest
class TestEnttiyRepositoryTest {
#Autowired
private TestEnttiyRepository testEnttiyRepository;
#Commit
#Test
void test() {
TestEntity testEntity = new TestEntity(1L, 1L);
TestEntity save = testEnttiyRepository.save(testEntity);
System.out.println("save = " + save);
}
}
Test Result
Hibernate:
select
testentity0_.code as code1_5_0_,
testentity0_.group_code as group_co2_5_0_,
testentity0_.mod_date as mod_date3_5_0_,
testentity0_.reg_date as reg_date4_5_0_
from
test_entity testentity0_
where
testentity0_.code=?
and testentity0_.group_code=?
save = TestEntity(groupCode=1, code=1, regDate=null, modDate=null)
Hibernate:
insert
into
test_entity
(reg_date, code, group_code)
values
(?, ?, ?)
Currently my question here is this.
The order of the output value through System.out.println and the insert query through save is reversed.
The LocalDateTime was not added automatically.
The order of the output value through System.out.println and the insert query through save is reversed.
Repository.save just as EntityManager.persist don't save anything.
They just make entities managed which will result in them being saved when a flush happens, which is at the end of the transaction, which in turn is at the end of your method, so after the System.out statement.

JPARepository - delete using date comparison with derived query

I'm trying to use JPARepository in Spring Boot to delete records that are less than a certain date, for for a given userid
Should be something like this Delete * from [table] where expiration_date < [date] and userid = [userid]
I thought I should be able to use one of the automatically generated methods
int deleteByExpiryDateBeforeAndUser(Date date, User user);
But this is generating a Select and not a Delete. What am I doing wrong?
Update
Entity class
#Getter
#Setter
#ToString
#Entity(name = "refresh_token")
public class RefreshToken {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToOne
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
#Column(nullable = false, unique = true)
private String token;
#Column(nullable = false)
private Date expiryDate;
public RefreshToken() {
}
}
Repository class
#Repository
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
Optional<RefreshToken> findByToken(String token);
#Modifying
void deleteByUserIdAndExpiryDateBefore(Long userId, Date expiryDate);
int deleteByUser(User user);
}
Here's how I'm calling it
#Transactional
public void deleteExpiredTokens(User user) {
refreshTokenRepository.deleteByUserIdAndExpiryDateBefore(user.getId(), new Date());
}
You see a select statement because Spring Data first loads entities by condition.
Then once entities became 'managed' Spring Data issues a delete query for each entity that was found.
If you want to avoid redundant SQL query - you have to consider #Query annotation.
Then your code will look like this:
#Repository
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
// ...
#Query(value = "DELETE FROM refresh_token WHERE user_id =:userId AND expiry_date < :expiryDate", nativeQuery = true)
#Modifying
void deleteByUserIdAndExpiryDateBefore(Long userId, Date expiryDate);
//...
}

JPA SQLResultSetMapping for SQL Aliases not Working At All?

Native SQL with aliased field names + remapping to receive managed entities is required for more complex queries with joined tables.
However, the mapping of the SQL aliases leads to an exception where the aliased fields cannot be found. Can anybody detect an error in the code below, or is SQLResultSetMapping broken? (The sample below is intentionally simple to allow quick checking)
RDBMS H2, DDL
create table A(
ID INTEGER DEFAULT NOT NULL AUTO_INCREMENT PRIMARY KEY,
VAL VARCHAR(10)
);
insert into A (val) values ('val1');
insert into A (val) values ('val2');
Java class
#Entity
#NamedNativeQuery(name = "queryall",
query="select ID as AID, val from A",
resultSetMapping = "mapping")
#SqlResultSetMapping(name = "mapping",
entities = #EntityResult(
entityClass = A.class,
fields = {#FieldResult(name = "ID", column = "AID")})
)
public class A implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Integer id;
#Column(name = "VAL")
private String val;
public A() {
}
public A(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getVal() {
return val;
}
public void setVal(String val) {
this.val = val;
}
#Override
public String toString() {
return "entities.A[ id=" + id +", val="+val+ " ]";
}
public static void main(String[] args) {
EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("JavaApplication6PU");
EntityManager em = entityManagerFactory.createEntityManager();
Query sqlQuery = em.createNamedQuery("queryall");
List list = sqlQuery.getResultList();
for (Iterator<A> iterator = list.iterator(); iterator.hasNext();) {
a = iterator.next();
System.out.println(String.format("entity %s, managed: %s", a, em.contains(a)));
}
}
}
Execution stops with exception:
[EL Warning]: 2018-01-12 21:45:42.748--UnitOfWork(1823014131)--Exception
[EclipseLink-6044] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd):
org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row [DatabaseRecord(
A.ID => null
A.VAL => val1)] during the execution of the query was detected to be null.
Primary keys must not contain null.
Query: ResultSetMappingQuery(name="queryall" referenceClass=A sql="select ID as AID, val from A")
This, in other words, means: No mapping has taken place -> aliased fields not found
The same when the mapping is announced in adhoc Queries.
Query sqlQuery = em.createNativeQuery("select ID as AID, val from A","mapping");
If resultClass is used instead of resultSetMapping and no SQL aliases exist, the output is as it should be. (This proves that there is no misspelling of fields or any other error)
#NamedNativeQuery(name = "queryall",
query="select ID, val from A",
resultClass = A.class)
Output:
entity entities.A[ id=1, val=val1 ], managed: true
entity entities.A[ id=2, val=val2 ], managed: true

Exception when selecting specific columns using Hibernate and Spring Data JPA

I have a table that has a bytea column (named 'pdf') and I don't want to always select it, specially when I'm returning a list from the database, due to performance issues.
I use native queries with spring data inside the repository to solve these types of situations before (when I used eclipselink), but with Hibernate, if I don't write all the columns in the query, it throws an exception.
For test purposes, I'm trying to select only the id from the User and I still get the exception.
Example: "SELET user.id FROM user WHERE user.id = '1'"
It throws an exception saying that it did not find name in the ResultSet, if I put name in the SQL, it then says age was not found and so on, until I have to write all the columns in the SQL.
Thanks in advance for any help.
What I have tried already:
Updating/Downgrading Hibernate and Spring Data with no luck.
Creating a new entity with only the columns I need, works, but it's a messy solution for me.
Maybe the problem is the combination of the frameworks I use and the way I use them, if someone wants, I could try to upload my whole project structure.
My code:
Entity
#Entity
#Table(name = "user", schema = "portal")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "pdf")
private byte[] pdf;
#Column(name = "name")
private String name;
#Column(name = "age")
private Integer age;
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public byte[] getPdf() {
return pdf;
}
public void setPdf(byte[] pdf) {
this.pdf = pdf;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Anexo)) {
return false;
}
Anexo other = (Anexo) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "br.gov.to.secad.portal.domain.User[ id=" + id + " ]";
}
}
Service
#Service
#Transactional(readOnly = true)
public class UserService implements Serializable {
private static final long serialVersionUID = 1L;
#Autowired
private IUserRepository userRepository;
public UserService() {
}
public User findOne() {
return userRepository.findOneSQL();
}
}
Repository
public interface IUserRepository extends JpaRepository<User, Serializable>, JpaSpecificationExecutor {
#Query(value = "SELECT user.id FROM user WHERE user.id = '1'", nativeQuery = true)
public User findOneSQL();
}
The exception:
org.postgresql.util.PSQLException: The column name name was not found in this ResultSet.
Solution
The solution is using an array of Object when I want to select anything less than what I've mapped on my Entity class, thats the limitation of Hibernate that I now understand.
So basically, the method will return Object[] and then I can iterate each position and instantiate a new entity of User with these values.
Example:
#Query(value = "SELECT user.id FROM user WHERE user.id = '1'", nativeQuery = true)
public Object[] findOneSQL();
I have faced the same problem, I know it is late but well there is a solution that I found elegant.
By the Spring documentation you can declare an interface and from here take the fields you want, in my case it has been something similar to this.
The interface to minimize the fields:
public interface CountryMinify {
String getName();
String getNameTranslation();
}
And my JpaRepository
public interface PlanetRepository extends JpaRepository<Planet, Long> {
#Query(value = "select p.name_country as name, p.name_country_translation as nameTranslation from vm_planet p where gid = ?1", nativeQuery = true)
CountryMinify findByCode(String codeCountry);
}
Keep in mind that the columns should be called the same as gos getter. For example: column name_country -> AS name and the getter of the interface is getName()
Try this
#Query(value = "SELECT user.id FROM user WHERE user.id = '1'", nativeQuery = true)
Integer findOneSQL();
Call the method like so
Integer user = userRepository.findOneSQL();
Edit 1 :
Since you are using native query you wont be able to use Projections which is a great way of accessing only certain entity fields. There is a JIRA ticket which is still under investigation.
Solution
Return List from your repository like so
#Query(value = "SELECT user.id, user.name FROM user WHERE user.id = '1'", nativeQuery = true)
List<Object[]> findOneSQL();
Iterate over the list of Objects and get your specific columns.
List<Object[]> userNative = userRepository.findOneSQL();
for (Object[] obj : userNative) {
System.out.println("User id : " + obj[0]);
System.out.println("User Name : " + obj[1]);
}

JPA with hibernate implementation is generating wrong named query

I configured JPA with spring. I am using spring 4.
I have an entity
#Entity
#NamedQueries({
#NamedQuery(name="PartnerCourseMapping.findByPartnerCourseIdAndHandlerName", query="select pm from PartnerCourseMapping pm where pm.partnerCourseId=:partnerCourseId and pm.handlerName=:handlerName")
})
#Table(name="PARTNER_COURSE_MAPPING")
public class PartnerCourseMapping implements Serializable {
private static final long serialVersionUID = 1L;
#Id
protected Long id;
#Column(name="COURSE_ID")
protected Long courseId;
#Column(name="PARTNER_COURSE_ID")
protected String partnerCourseId;
#Column(name="PARTNER_ID")
protected Integer partnerId;
#Column(name="PRODUCT_TYPE")
protected String productType;
#Column(name="HANDLER_NAME")
protected String handlerName;
//getters and setters
}
I have another entity which i defined like below
#Entity
#NamedNativeQueries({
#NamedNativeQuery(
name="ExternalCourse.findExternalCourseMappingByLearningSessionGuid",
query="SELECT PCM.*, LE.id AS LearnerEnrollmentId, LE.LEARNER_ID AS LearnerId "
+ "FROM LEARNINGSESSION LS "
+ "INNER JOIN LEARNERENROLLMENT LE ON LE.ID = LS.ENROLLMENT_ID "
+ "INNER JOIN PARTNER_COURSE_MAPPING PCM ON PCM.COURSE_ID = LE.COURSE_ID "
+ "WHERE LS.LEARNINGSESSIONGUID = :learningSessionGuid",
resultSetMapping="externalCourseMapping"
)
})
#SqlResultSetMappings({
#SqlResultSetMapping(
name="externalCourseMapping",
classes = {
#ConstructorResult(targetClass = ExternalCourse.class,
columns={
#ColumnResult(name = "ID", type=Long.class ),
// remaing ColumnResult
}
)
}
)
})
public class ExternalCourse extends PartnerCourseMapping /*implements Serializable*/ {
private Long learnerEnrollmentId;
private Long learnerId;
//default constructor
public ExternalCourse(Long id, Long courseId, String partnerCourseId, Integer partnerId, String productType,
String handlerName, Long learnerEnrollmentId, Long learnerId) {
this.id = id;
// remaing values
}
//getters and setters for learnerEnrollmentId and learnerId
}
Now I query PartnerCourseMapping.findByPartnerCourseIdAndHandlerName
TypedQuery<PartnerCourseMapping> query = entityManager.createNamedQuery("PartnerCourseMapping.findByPartnerCourseIdAndHandlerName", PartnerCourseMapping.class);
query.setParameter("partnerCourseId", paernerCourseId);
query.setParameter("handlerName", handlerName);
return getResult(query);
protected T getResult(TypedQuery<T> query) {
List<T> list = query.getResultList();
return CollectionUtils.isEmpty(list) ? null : list.get(0);
}
Hibernate is generating exception that
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
...
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Invalid column name 'learnerEnrollmentId'.
When I debug then I saw hibernate is generating query like below
select partnercou0_.id as id2_13_,
partnercou0_.COURSE_ID as COURSE_I3_13_,
partnercou0_.HANDLER_NAME as HANDLER_4_13_,
partnercou0_.PARTNER_COURSE_ID as PARTNER_5_13_,
partnercou0_.PARTNER_ID as PARTNER_6_13_,
partnercou0_.PRODUCT_TYPE as PRODUCT_7_13_,
partnercou0_.learnerEnrollmentId as learnerE8_13_,
partnercou0_.learnerId as learnerI9_13_,
partnercou0_.DTYPE as DTYPE1_13_
from PARTNER_COURSE_MAPPING partnercou0_ where partnercou0_.PARTNER_COURSE_ID=? and partnercou0_.HANDLER_NAME=?
I want to ask that why hibernate is including learnerEnrollmentId and learnerId column? I am passing the query name and query. If I refactor my code like below then I get the correct result
#Entity
#NamedNativeQueries({
#NamedNativeQuery(
name="ExternalCourse.findExternalCourseMappingByLearningSessionGuid",
...
resultSetMapping="externalCourseMapping"
)
})
#SqlResultSetMappings({
#SqlResultSetMapping(
name="externalCourseMapping",
classes = {
..
}
)
})
public class ExternalCourse implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
private Long courseId;
private String partnerCourseId;
private Integer partnerId;
private String productType;
private String handlerName;
private Long learnerEnrollmentId;
private Long learnerId;
//default constructor
//constructor with all parameters
//getters and setters
}
Why I am getting exception when I am extending class? I am passing the query name. Why ?
Thanks