JPA query receive column name not valid - spring-data-jpa

I need to execute a very simply JPA Query
but I receive the error message "column name not valid".
Obviously, if I execute manually the query it works fine, but not if I use JPA.
This is my repository class:
#Repository
public interface OrdersRepositoryPayId extends JpaRepository<OrdersEntityPayId, Long> {
#Query(value="select trackid from orders where payid=:payid" , nativeQuery=true)
OrdersEntityPayId getOrdersByPaymentId(#Param("payid") String payid);
}
And this is the Entity class:
#Entity
#Table(name = "ORDERS")
public class OrdersEntityPayId {
#Id
private Long orderid; // These are the table fields name
private String payid;
private String trackid;
// Getter and setter ....
}
And this is the oracle tabel primaryKey:
Database is Oracle. Table name is ORDERS. PrimaryKey is ORDERS.ORDERID NUMBER(16,0)
Could you pls indicate what I'm wrong?
Below the error I received:
org.springframework.orm.jpa.JpaSystemException: could not execute
query; nested exception is
org.hibernate.exception.GenericJDBCException: could not execute query
at
org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:331)
at
org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)
at
org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)
at
org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at
org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
[..] [..] Caused by: java.sql.SQLException: Nome colonna non valido
at
oracle.jdbc.driver.OracleStatement.getColumnIndexPrimitive(OracleStatement.java:4257)
at
oracle.jdbc.driver.OracleStatement.getColumnIndex(OracleStatement.java:4215)
at
oracle.jdbc.driver.InsensitiveScrollableResultSet.findColumn(InsensitiveScrollableResultSet.java:299)
at
oracle.jdbc.driver.GeneratedResultSet.getLong(GeneratedResultSet.java:626)
[...]
Thanks in advance.

Related

How can I use JPA Query Methods to return an OR condition with NULL?

Am trying to create a Query that either matches all rows that equal tier or are NULL. Using Query Methods as described in Spring JPA Docs. The Default implementation below works if I just pass in the tier:-
#Entity
#Table(name = "tier")
class UuTier {
Long id;
Long tierId;
}
#Entity
#Table(name = "user")
class User {
#OneToOne
#JoinColumn(name="tier_id")
UuTier uuTier;
// Other Relationships
}
public interface UserRepository extends Repository<User, Long> {
List<User> findByTier_Id(#Param("tier")Long tier);
}
What I need is something like this, which is throwing an error " No property null found for type User". Can I achieve this ask using Query Methods?:-
public interface UserRepository extends Repository<User, Long> {
List<User> findByTierOrNull_Id(#Param("tier")String tier);
}
Following up from one of the responders (who for some reason deleted her post) - I got this to work!!
#Query("SELECT entity FROM User entity LEFT JOIN UuTier uuTier ON entity.uuTier.tier = uuTier.tier"
+ " WHERE entity.uuTier.tier = :tier OR entity.uuTier.tier IS NULL")
public List<User> findByTierOrNull_Id(#Param("tier") Long tier);

How to test a jpa readonly query?

The model is simplified for the question.
I have this entity:
#Entity
public class Formation {
#Id
Long id;
String login;
String code;
String level;
// geters and Setters
With this repository:
public interface FormationRepository extends JpaRepository<Formation, Long> {
#Query(value = "SELECT UNIQUE l.id, l.login, d.code,d.level\n"
" FROM table_login l,\n" +
" table_diploma d,\n" +
" WHERE
" l.fhab_key = d.fhab_key\n" +
" AND l.login= :login", nativeQuery = true)
List<Formation> findAllByLogin(#Param("login")String login);
So far so good, this works.
Now I want to add test for the repository (with and h2database). But I can't save data, as the entity isn't mapped to a single table.
So this won't work:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class FormationRepositoryTest {
#Autowired
FormationRepository formationRepository;
#Test
public void communeRepositoryTest() {
Formation formation = new Formation();
formation.setId(123L);
formation.setDlog_login("123");
formationRepository.save(formation); // ok
formationRepository.findAllByLogin("123"); // ko -> Caused by: Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: scolarite.scol_droit_login is not mapped
}
}
Here I need to add, that I don't want sql files in my app.
So what would be a solution here ?
Change my model to create an entity by table ? (in real, my request use 8 inner joins, so it will be quite long to code all that...)
Another solution ?
If you do not want to create the entities for all your tables, you could pre-populate your testing H2 database by defining schema.sql and data.sql on your classpath.
schema.sql will contain the DDL statements to create all tables involved (you mention 8 tables).
data.sql will contain the insert statements needed to make your custom sql return login = 123.
You can find an example here.
Then your test code would be like:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class FormationRepositoryTest {
#Autowired
FormationRepository formationRepository;
#Test
public void communeRepositoryTest() {
List<Formation> formations = formationRepository.findAllByLogin("123");
Assert.assertEquals(formations.size(), 1);
}
}

how to filter out entity object inside entity in rest api

I am using Spring Boot to implement rest api. There are three entities SeqTb, PairTb, and GroupTb and they are nested. SeqTb has manytoone with PairTb. PairTb has onetomany relationship with SeqTb and also manytoone with GroupTb.
//SeqTb.java
#Entity
#Table(name="SEQ_TB")
public class SeqTb implements Serializable {
.......
#ManyToOne
#JoinColumn(name="PAIR_ID")
private PairTb pairTb;
......
}
// PairTb.java
#Entity
#Table(name="PAIR_TB")
#NamedQuery(name="PairTb.findAll", query="SELECT p FROM PairTb p")
public class PairTb implements Serializable {
#ManyToOne
#JoinColumn(name="GROUP_ID")
private GroupTb groupTb;
#OneToMany(mappedBy="pairTb", cascade=CascadeType.ALL)
private List<SeqTb> seqTbs;
}
//GroupId.java
#Entity
#Table(name="GROUP_TB")
public class GroupTb implements Serializable {
//bi-directional many-to-one association to PairTb
#OneToMany(mappedBy="groupTb", cascade=CascadeType.ALL)
private List<PairTb> pairTbs;
}
In my controller GET request with analysisId was handled in the following way:
#RequestMapping(
value = "/api/seqs/{analysis_id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SeqTb> getSeqByAnalysisId(#PathVariable("analysis_id") String analysis_id) {
SeqTb seq = seqService.findByAnalysisId(analysis_id);
return new ResponseEntity(seq, HttpStatus.OK);
}
I also create a bean class SeqServiceBean that extends the interface SeqService which in turn calls methods from the following JPA repository for query.
//SeqRepository.java
#Repository
public interface SeqRepository extends JpaRepository<SeqTb, Integer> {
#Override
public List<SeqTb> findAll();
public List<SeqTb> findByAnalysisId(String analysisId);
}
When I query a SeqTb object with SeqTb.PairTb == null, the api works just fine. However, if the analysisId I put in the url belongs to a SeqTb record that associates with a pairId which in turn belongs to a groupId, the program would go nuts. Below is the output, the first part output is correct (bold text). After that it keeps printing PairTb and GroupTb in loops (repeating keywords pairTb, groupTb).
{"rowId":8,"analysisId":"cce8d2c2-a6dc-4ee9-ba97-768f058abb50","analyteCode":"D","center":"UCSC",
"pairTb":{"rowId":4,"pairCode":"01ad975d-c2ed-4e4d-bd3b-c9512fc9073c","groupTb":{"rowId":1,"groupName":"PAWG_pilot-50","pairTbs":[{"rowId":1,"pairCode":"00ad0ffe-2105-4829-a495-1c2aceb5bb31","groupTb":{"rowId":1,"groupName":"PAWG_pilot-50","pairTbs":
Meanwhile I got lots of errors from tomcat server:
Caused by: java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:565) ~[tomcat-embed-core-8.0.32.jar:8.0.32]
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:212) ~[tomcat-embed-core-8.0.32.jar:8.0.32]
How do I ignore the nested entity object inside an entity and get only the meaning columns?
You can also annotate a property with #JsonIgnore in order to not output that field.
Found the solution. Created a value object that only contains the specific columns from entity and leave out the nested entity object. And it works.

Assigning Sequences for all JPA entities using SessionCustomizer

I am trying to use a SessionCustomizer to automatically generate Sequences in EclipseLink which already exist in the database following a special naming convention. For example an entity called Item is mapped to a table called ITEMS which has a four letter alias ITEM and a database sequence called ITEM_ID_SEQ for unique ID generation.
I am using an annotation as a marker to hold the alias name on the entity class because we are using it for other purposes, too:
package jpa.namingsupport;
// imports omitted
#Target(TYPE)
#Retention(RUNTIME)
public #interface Alias {
String name();
}
Entities look like this:
package jpa.entities;
// imports omitted
#Entity
#Table(name = "ITEMS")
#Alias(name = "ITEM")
public class Item {
#Id
private Long id;
#Version
private Long version;
private String name;
// setters and getters omitted
}
Using a SessionCustomizer registered correctly and verified running on startup to create and add the Sequences to the entities:
package jpa.namingsupport;
// imports omitted
public class AliasCustomizer implements SessionCustomizer {
#Override
public void customize(Session session) throws Exception {
Map<Class, ClassDescriptor> entities = session.getDescriptors();
for (Class entity : entities.keySet()) {
customizeSequence(aliasNameFor(entity), entities.get(entity), session);
}
}
private String aliasNameFor(Class entity) {
Alias alias = (Alias) entity.getAnnotation(Alias.class);
return alias.name();
}
private void customizeSequence(String alias, ClassDescriptor descriptor, Session session) {
NativeSequence sequence = new NativeSequence(underscores(alias, "ID", "SEQ"), 1);
session.getLogin().addSequence(sequence);
descriptor.setSequenceNumberName(sequence.getName());
descriptor.setSequenceNumberField(descriptor.getPrimaryKeyFields().get(0));
descriptor.setSequence(sequence);
}
private String underscores(String... parts) {
return StringUtils.arrayToDelimitedString(parts, "_");
}
}
But when I am running my tests the ID is not assigned from the Sequence before saving:
[EL Warning]: 2013-07-14 20:32:32.571--UnitOfWork(1908148255)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.h2.jdbc.JdbcSQLException: NULL nicht zulässig für Feld "ITEM_ID"
NULL not allowed for column "ITEM_ID"; SQL statement:
INSERT INTO ITEMS (ITEM_NAME, ITEM_VERSION) VALUES (?, ?) [23502-172]
Any hints and ideas what I am missing in my code? What I am seeing is that there is no reference to the ITEM_ID column in the generated insert statement.
Why don't you just put #GeneratedValue(strategy=SEQUENCE, generator="ITME_ID_SEQ") on your id?
For your customizer, don't call descriptor.setSequence(), this should be done be initializaiton.
The SQL is expecting the id to being using an IDENTITY value, you need to configure your table for this. If you want to use SEQUENCE instead, then pass false into new NativeSequence(name, increment, false). H2 supports both IDENTITY and SEQUENCE, and NativeSequence defaults to using IDENTITY, false means SEQUENCE.

PersistenceException from a simple CRUD for a simple class

Using the in-memory database (db=mem), I'm simply trying to insert a new object into the database using CRUD.
The class is as follows:
#Entity
public class ActivityModel extends Model {
#Required
public String description;
#Required
public String link;
#Required
public Date timestamp;
public ActivityModel(String description, String link, Date timestamp) {
this.description = description;
this.link = link;
this.timestamp = timestamp;
}
}
And the CRUD controllers looks like this:
#CRUD.For(ActivityModel.class)
public class Activities extends CRUD {
}
Yet, when I try to add a new activity to the database, I get the following error:
13:48:20,710 INFO ~ Application 'ScoreDB' is now started !
13:48:48,323 WARN ~ SQL Error: -177, SQLState: 23000
13:48:48,323 ERROR ~ Integrity constraint violation - no parent FKF0DCD93AADF686FF table: USERMODEL in statement [insert into ActivityModel (id, description, link, timestamp) values (null, ?, ?, ?)]
13:48:48,370 ERROR ~
#65pagp52g
Internal Server Error (500) for request POST /admin/activities
Execution exception (In {module:crud}/app/controllers/CRUD.java around line 135)
PersistenceException occured : org.hibernate.exception.ConstraintViolationException: could not insert: [models.users.hidden.ActivityModel]
play.exceptions.JavaExecutionException: org.hibernate.exception.ConstraintViolationException: could not insert: [models.users.hidden.ActivityModel]
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:290)
at Invocation.HTTP Request(Play!)
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not insert: [models.users.hidden.ActivityModel]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1235)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1168)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1174)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:674)
at play.db.jpa.JPABase._save(JPABase.java:37)
at controllers.CRUD.create(CRUD.java:135)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:413)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:408)
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:182)
... 1 more
... etc ...
What am I doing wrong? :S
Apparently it was because there was a different class containing the following code:
#OneToMany(cascade = CascadeType.ALL, mappedBy="id")
public Collection<ActivityModel> activities;
In which the 'mappedBy="id"' is the source of the problem.
Remove this and it all works :)