NamedNativeQuery JPA null primary keys exception - postgresql

I created a named query that looks like this with a result set mapping:
#NamedNativeQueries({ #NamedNativeQuery(name = "Q_INSTRUMENTS", query = "SELECT i.ID, i.TICKER, i.ISIN, i.SEDOL, i.NAME, i.COUNTRY_ID, i.CONTRACT_SIZE, i.EXPIRY_DATE, i.TYPE_ID FROM INSTRUMENT i INNER JOIN COUNTRY c ON i.COUNTRY_ID = c.ID"
+ " GROUP BY i.ID, i.TICKER, i.ISIN, i.SEDOL, i.COUNTRY_ID, i.CONTRACT_SIZE, i.EXPIRY_DATE, i.TYPE_ID, c.NAME HAVING i.ID = MAX(i.ID) ORDER BY i.NAME, c.NAME ASC", resultClass = Instrument.class, resultSetMapping = "InstrumentMapping") })
#SqlResultSetMapping(name = "InstrumentMapping", entities = { #EntityResult(entityClass = Instrument.class, fields = {
#FieldResult(name = "id", column = "ID"), #FieldResult(name = "ticker", column = "TICKER"),
#FieldResult(name = "sedol", column = "SEDOL"), #FieldResult(name = "isin", column = "ISIN"),
#FieldResult(name = "name", column = "NAME"), #FieldResult(name = "countryId", column = "COUNTRY_ID"),
#FieldResult(name = "contractSize", column = "CONTRACT_SIZE"),
#FieldResult(name = "expiryDate", column = "EXPIRY_DATE"), #FieldResult(name = "typeId", column = "TYPE_ID") }) })
and here is the class that is annotated with this named query
public class Instrument extends ManagedEntityBase{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(length = 10)
private String ticker;
#Column(length = 30)
private String isin;
#Column(length = 10, unique = true, nullable = false)
private String sedol;
#Column(length = 60, nullable = false)
private String name;
#Column(name = "COUNTRY_ID", nullable = false, insertable = false, updatable = false)
private long countryId;
#Column(name = "TYPE_ID", nullable = false, insertable = false, updatable = false)
private byte typeId;
#Column(name = "CONTRACT_SIZE", nullable = false)
private Long contractSize;
#Transient
private String contractSizeString;
#Temporal(TemporalType.DATE)
#Column(name = "EXPIRY_DATE")
private Date expiryDate;
public Instrument() {
// Default constructor
this.contractSize = 1L;
}
ublic Instrument(String sedol, Country country, InstrumentType type) {
this();
this.sedol = sedol;
this.country = country;
this.type = type;
}
/**
* Creates an {#link Instrument} with the given sedol, isin, country and type.
*
* #param sedol instrument sedol
* #param isin instrument isin
* #param country instrument country
* #param name
* #param ticker
* #param type instrument type
*/
public Instrument(String sedol, String isin, Country country, String name, String ticker, InstrumentType type) {
this(sedol, country, type);
this.isin = isin;
this.name = name;
this.ticker = ticker;
}
}
When I try to call the name query:
public static List<Instrument> getInstruments() {
return DatabaseUtility.getEntityManager().createNamedQuery("Q_INSTRUMENTS").getResultList();
}
I get the following error:
[EL Warning]: 2020-01-31 15:55:40.732--UnitOfWork(1788318093)--Exception [EclipseLink-6044] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row [ArrayRecord(
=> 3000000002285
=> 00577
=> BMG8827A1045
=> BYX9N24
=> 13 HOLDINGS LTD, THE
=> 2
=> 1
=> null
=> 1)] during the execution of the query was detected to be null. Primary keys must not contain null.
Query: ReadAllQuery(name="Q_INSTRUMENTS" referenceClass=Instrument sql="SELECT i.ID, i.TICKER, i.ISIN, i.SEDOL, i.NAME, i.COUNTRY_ID, i.CONTRACT_SIZE, i.EXPIRY_DATE, i.TYPE_ID FROM INSTRUMENT i INNER JOIN COUNTRY c ON i.COUNTRY_ID = c.ID GROUP BY i.ID, i.TICKER, i.ISIN, i.SEDOL, i.COUNTRY_ID, i.CONTRACT_SIZE, i.EXPIRY_DATE, i.TYPE_ID, c.NAME HAVING i.ID = MAX(i.ID) ORDER BY i.NAME, c.NAME ASC")
In the table the expiry_date is indeed null but this does not take part from primary key and by default the nullable value is true for expiry date.
I use EclipseLink with PostreSQL.
I try to migrate the current code from old Sybase to new PostreSQL. With Sybase datasource the error does not happen.
I tried using
<property name="eclipselink.jpa.uppercase-column-names" value="true"/>
to make sure the mapping to the columns is not made wrong, but it didn't fix my issue

It seems that my question had its answer in it. The problem was due to wrong mapping for id. The mapping was done using "id" but the column in database is ID (capitalized). That's why I had null for id.
The solution was to put
<property name="eclipselink.jpa.uppercase-column-names" value="true"/>
in the right persistance-unit.

Related

Spring Data Native Query uses VARBINARY for null String #Param

I have a JpaRepository, with a native query in it:
#Query (value = "SELECT t.team_name FROM teams t " +
"WHERE (:teamcode IS NULL OR t.team_code = :teamcode) ",
nativeQuery = true)
List<Object[]> getTeamNames(#Param("teamcode") #Nullable String teamCode);
If I pass a non-null value, it works fine - the binding parameter type is VARCHAR. However, if I pass in a null parameter value, then the binding type is VARBINARY, and the query fails with the error:
SQL Error: 0, SQLState: 42883
ERROR: operator does not exist: character varying = bytea
Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
Table:
CREATE TABLE teams (
team_id BIGINT NOT NULL,
team_code VARCHAR(24) NOT NULL,
team_name VARCHAR(200),
team_desc VARCHAR(2000),
)
Entity:
#Entity
#DynamicUpdate
#Getter
#Setter
#EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
#Table(name = "teams", schema = "public")
public class Team extends BaseEntity
{
#Id
#Column(name = "team_id", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ent_generator")
#SequenceGenerator(name = "ent_generator", sequenceName = "entity_seq", allocationSize = 1)
private long teamId;
#EqualsAndHashCode.Include
#Basic
#Column(name = "team_code", length = 24)
private String teamCode;
#Basic
#Column(name = "team_name", length = 200)
private String teamName;
#Basic
#Column(name = "team_desc", length = 2000)
private String teamDescription;
}
How do I resolve this?

Postgres Bit Variant equivalent data type in Java

I Have a Postgres sql database with data in it. Im trying to insert data from my Spring-Data-JPA project with Hibernate mappings. I am getting
ERROR: org.hibernate.engine.jdbc.spi.SqlExceptionHelper - ERROR: column "is_active" is of type bit varying but expression is of type oid
Here is my entity
#Entity
#Table(schema = "shard_1", name = "pages")
#XmlRootElement
#NamedQueries({ #NamedQuery(name = "Pages.findAll", query = "SELECT p FROM Pages p"),
#NamedQuery(name = "Pages.findById", query = "SELECT p FROM Pages p WHERE p.id = :id"),
#NamedQuery(name = "Pages.findByPageNumber", query = "SELECT p FROM Pages p WHERE p.pageNumber = :pageNumber"),
#NamedQuery(name = "Pages.findByTitle", query = "SELECT p FROM Pages p WHERE p.title = :title"),
#NamedQuery(name = "Pages.findBySectionId", query = "SELECT p FROM Pages p WHERE p.sectionId = :sectionId"),
#NamedQuery(name = "Pages.findByVersionId", query = "SELECT p FROM Pages p WHERE p.versionId = :versionId"),
#NamedQuery(name = "Pages.findByCreated", query = "SELECT p FROM Pages p WHERE p.created = :created"),
#NamedQuery(name = "Pages.findByModified", query = "SELECT p FROM Pages p WHERE p.modified = :modified"),
#NamedQuery(name = "Pages.findByPdfcontent", query = "SELECT p FROM Pages p WHERE p.pdfcontent = :pdfcontent") })
public class Pages implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(nullable = false)
private Long id;
#Column(name = "page_number")
private Integer pageNumber;
#Column(length = 2147483647)
private String title;
#Column(name = "section_id")
private BigInteger sectionId;
#Column(name = "version_id")
private BigInteger versionId;
#Lob
#Column(name = "is_active")
private Object isActive;
#Temporal(TemporalType.TIMESTAMP)
private Date created;
#Temporal(TemporalType.TIMESTAMP)
private Date modified;
#Column(length = 2147483647)
private String pdfcontent;
}
This is my postgres table
CREATE TABLE shard_1.pages
(
id bigint NOT NULL DEFAULT shard_1.id_generator(),
page_number integer,
title text COLLATE pg_catalog."default",
section_id bigint,
version_id bigint,
is_active bit varying,
created timestamp without time zone,
modified timestamp without time zone,
pdfcontent text COLLATE pg_catalog."default",
CONSTRAINT pages_pkey PRIMARY KEY (id)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE shard_1.pages
OWNER to root;
I tried Java datatypes boolean, string, char array, int, byte[] but fails with everything. What datatype for the field is_active should I use here?
Try to remove #Lob , i.e :
#Column(name = "is_active")
private Boolean isActive;
And alter your table 'shard_1.pages' just like this :
ALTER TABLE shard_1.pages DROP COLUMN is_active;
ALTER TABLE shard_1.pagese ADD COLUMN is_active boolean;

Default Sort on a Spring Data JPA Repository Method with Custom Query and Pageable Parameter

I have the following repository method that works exactly the way I need it to iff the user provides a sort column in the page parameter:
public interface IdentityRepository extends JpaRepository<Identity, String> {
#Query("select distinct ident from Identity ident left outer join ident.authorities authority "
+ "where ("
+ "(:src is null or ident.source = :src) and "
+ "(:org is null or ident.organization = :org) and "
+ "(:auth is null or authority.authority = :auth) and "
+ "(:authSrc is null or authority.authoritySource = :authSrc))")
#RestResource(path="filter")
public Page<Identity> findWithFilter(
#Param("src") String source,
#Param("org") String org,
#Param("auth") Authority auth,
#Param("authSrc") AuthoritySource authSrc,
Pageable page);
...
}
If the caller provides a page count, but not a sort column, they will get back the correct number of results when retrieving all the pages. However, many of the entities will be duplicated, so even though the result count is correct, many expected entities are missing and others are duplicated (or triplicated).
What I'm wondering is if there is a way to provide a default sort column and direction if the user does not specify one. I've learned that #EnableSpringDataWebSupport can help here, but we're not using Spring MVC, so I don't have any controllers to attach the #SortDefaults to. We are using Spring Data Rest though. Also, I've tried changing the method name to findWithFilterOrderByIdAsc, but that did not seem to help. Ran across this issue in the Spring JIRA, which I believe is exactly what I need, but until it's resolved, does anyone know of a work around?
Here's my entity...
#Entity
#Table(name = "identity", indexes = { #Index(columnList = "user_id", unique = true) })
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Audited
public class Identity implements Serializable, Identifiable<String> {
/**
* The unique identifier for this identity within the IDD application.
*/
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "IDDUidGenerator")
#GenericGenerator(name = "IDDUidGenerator")
private String id;
/**
* The name of the identity provider wherein this identity is originally defined.
*/
#Column(name = "source")
private String source = INTERNAL_SOURCE;
/**
* The unique identifier for this identity within the customer's identity provider.
*/
#NotNull
#Column(name = "user_id", nullable = false, unique = true)
private String userId;
/**
* The roles this identity is authorized to perform.
*/
#OneToMany(fetch = FetchType.EAGER, mappedBy = "identity", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<IdentityAuthority> authorities = new HashSet<>();
...
}
And its sub-entity...
#Entity
#Table(name = "identity_authority")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Audited
public class IdentityAuthority implements Serializable, Identifiable<Long> {
private static final long serialVersionUID = -5315412946768343445L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#XmlTransient
#JsonIgnore
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "identity_id", nullable = false)
#XmlTransient
#JsonIgnore
private Identity identity;
#Enumerated(EnumType.STRING)
#Column(name = "authority", length = 20, nullable = false)
private Authority authority;
#Enumerated(EnumType.STRING)
#Column(name = "authority_source", length = 30, nullable = false)
private AuthoritySource authoritySource;
...
}
Here's the test case I ran to demonstrate the problem...
#Test
public void testPagedRequestsReturnAllResults() {
// Create identities
String source = "One Hundred Identities Generator";
int numIdentities = 100;
int pageSize = 5;
List<Identity> input = new ArrayList<>();
for (int i=0; i<numIdentities; i++) {
Identity identity = new Identity();
identity.setUserId(UUID.randomUUID().toString());
identity.setSource(source);
input.add(identity);
}
// Save identities
List<Identity> output = repository.saveBulk(input);
Set<String> savedIds = collectIds(output, null);
assertThat(savedIds.size()).isEqualTo(numIdentities);
// Test Sorted Find Filter with Paging (THIS PASSES)
Pageable pageRequest = new PageRequest(0, pageSize, new Sort(Direction.ASC, "id"));
Set<String> foundPagedIds = new HashSet<>();
do {
Page<Identity> page = repository.findOrderByIdAsc(source, null, null, null, pageRequest);
List<Identity> foundIdentities = page.getContent();
foundPagedIds = collectIds(foundIdentities, foundPagedIds);
pageRequest = page.nextPageable();
} while (pageRequest != null);
assertThat(foundPagedIds.size()).isEqualTo(numIdentities);
assertThat(foundPagedIds).isEqualTo(savedIds);
// Test Unsorted Find Filter with Paging (THIS FAILS)
pageRequest = new PageRequest(0, pageSize);
foundPagedIds = new HashSet<>();
do {
Page<Identity> page = repository.findOrderByIdAsc(source, null, null, null, pageRequest);
List<Identity> foundIdentities = page.getContent();
foundPagedIds = collectIds(foundIdentities, foundPagedIds);
pageRequest = page.nextPageable();
} while (pageRequest != null);
assertThat(foundPagedIds.size()).isEqualTo(numIdentities);
assertThat(foundPagedIds).isEqualTo(savedIds);
}

jpa merge Predicate array + search for foreign key value

I would like to have a search like this:
Select * From Users where ( ( username Like %criteria% OR firstName Like %criteria% ...) AND (CarId = carId) )
I did with 2 Predicate combined into an array, but need to do it with 1 Predicate only
#Entity
#Table(name = "users", uniqueConstraints = { #UniqueConstraint(columnNames = { "username" }) })
public class User{
#Id
#GeneratedValue(generator = "seq_id_user", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "seq_id_user", sequenceName = "seq_id_user")
private Long id;
#Column(unique = true)
#NotNull
private String username;
#Column(name = "first_name")
private String firstName;
....
#ManyToOne
#JoinColumn(name = "car_id", nullable = false, foreignKey = #ForeignKey(name = "CAR_ID_FK"))
private Car car;
...
The Car is a similar class, the User has 1 foreign key.
#Override
public List<User> searchUsers(String criteria, Car car) {
//TODO: check params and return values based on test cases
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
Root<User> r = query.from(User.class);
Predicate predicate = builder.conjunction();
predicate = builder.or(predicate, builder.like(r.get("username"), "%" + criteria + "%"));
predicate = builder.or(predicate, builder.like(r.get("firstName"), "%" + criteria + "%"));
predicate = builder.or(predicate, builder.like(r.get("lastName"), "%" + criteria + "%"));
predicate = builder.or(predicate, builder.like(r.get("nickname"), "%" + criteria + "%"));
// how to implement the filter by foreign key value with AND? - can be a sub query to, which will be executed first time
// I need to use 1 Predicate, not a Predicate Array!
query.select(r).where(predicate);
List<User> result = entityManager.createQuery(query).getResultList();

Group By in Java Persistence/JPQL

I have an Entity class and it has #ManyToOne relationships. I need to use GROUP BY as in SQL query.
I have written a JPQL but its not working. My code is :
#NamedQuery(name = "AssetDepModel.findByAssedId",
query = "SELECT dep FROM AssetDepModel dep "
+ "JOIN dep.faDetails fad "
+ "WHERE fad.assetId.assId = :assetId_passed "
+ "GROUP BY dep.faDetails,dep.faDetails.id,dep.fiscalModel.fyId,dep.depAmt,dep.depId,dep.depMethodId,dep.depRate,dep.depTypeId,dep.quarterId,dep.createdDt,dep.createdBy,dep.updatedDt,dep.updatedby "
+ "ORDER BY fad.id")
public class AssetDepModel implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
public static final String FIND_BY_ASSET_ID = "AssetDepModel.findByAssedId";
public static final String FIND_BY_DETAIL_ID = "AssetDepModel.findByDetailId";
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "dep_id")
private int depId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "fiscal_id", referencedColumnName = "fy_id")
private FiscalYrModel fiscalModel;
#Column(name = "quarter_id")
private int quarterId;
#ManyToOne
#JoinColumn(referencedColumnName = "id", name = "fa_details_id")
private FADetailsModel faDetails;
#Column(name = "dep_type_id")
private int depTypeId;
#Column(name = "dep_method_id")
private int depMethodId;
#Column(name = "dep_rate")
private Double depRate;
#Column(name = "dep_amt")
private Double depAmt;
#Column(name = "created_dt")
#Temporal(TemporalType.TIMESTAMP)
private Date createdDt;
#Column(name = "created_by")
private int createdBy;
#Column(name = "updated_dt")
#Temporal(TemporalType.TIMESTAMP)
private Date updatedDt;
#Column(name = "updated_by")
private int updatedby;
I tried this code but while calling the JPQL it always gives error saying that objects in Select is not included in Group By clause.
I need to GROUP BY according to a foreign key field.
I get following error :
Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: Column
'inv_asset_depreciation.fa_details_id' is invalid in the select list because
it is not contained in either an aggregate function or the GROUP BY clause.
Error Code: 8120
Call: SELECT t0.dep_id, t0.created_by, t0.created_dt, t0.dep_amt, t0.dep_method_id,
t0.dep_rate, t0.dep_type_id, t0.quarter_id, t0.updated_dt, t0.updated_by,
t0.fa_details_id, t0.fiscal_id FROM inv_asset_depreciation t0, fiscal_yr t2,
inv_fixed_asset_detail_mcg t1 WHERE ((t1.asset_id = ?) AND ((t1.id = t0.fa_details_id)
AND (t2.fy_id = t0.fiscal_id))) GROUP BY t1.id, t1.asset_given_name,
t1.brand_name_description, t1.created_by, t1.created_date,
t1.dispose_dt_en,t1.dispose_dt_np, t1.dispose_value, t1.req_form_no,
t1.start_use_dt_en,t1.start_use_dt_np,t1.update_count, t1.updated_by,
t1.updated_date, t1.asset_id,t1.dept_id, t1.status, t1.id,t2.fy_id, t0.dep_amt,
t0.dep_id, t0.dep_method_id,t0.dep_rate, t0.dep_type_id,t0.quarter_id,
t0.created_dt, t0.created_by,t0.updated_dt, t0.updated_by
ORDER BY t1.id
bind => [1 parameter bound]
Query: ReportQuery(name="AssetDepModel.findByAssedId" referenceClass=AssetDepModel
sql="SELECT t0.dep_id, t0.created_by, t0.created_dt, t0.dep_amt,
t0.dep_method_id,t0.dep_rate,t0.dep_type_id, t0.quarter_id, t0.updated_dt,
t0.updated_by, t0.fa_details_id,t0.fiscal_id FROM inv_asset_depreciation t0,
fiscal_yr t2, inv_fixed_asset_detail_mcg t1 WHERE ((t1.asset_id = ?)
AND ((t1.id = t0.fa_details_id) AND (t2.fy_id = t0.fiscal_id)))
GROUP BY t1.id, t1.asset_given_name, t1.brand_name_description,
t1.created_by,t1.created_date, t1.dispose_dt_en, t1.dispose_dt_np,
t1.dispose_value, t1.req_form_no, t1.start_use_dt_en, t1.start_use_dt_np,
t1.update_count, t1.updated_by, t1.updated_date,t1.asset_id, t1.dept_id,
t1.status, t1.id, t2.fy_id, t0.dep_amt, t0.dep_id, t0.dep_method_id,
t0.dep_rate, t0.dep_type_id, t0.quarter_id, t0.created_dt,
t0.created_by, t0.updated_dt,t0.updated_by ORDER BY t1.id")
I modified a little bit like this :
#SuppressWarnings("unchecked")
public List<Object> findByAssetIdForSaleWriteOff(int assetId){
Query query = getEntityManager().createQuery("SELECT fad.id,dep.depAmt FROM AssetDepModel dep "
+ "JOIN dep.faDetails fad "
+ "WHERE fad.assetId.assId = "+assetId+" "
+ "GROUP BY fad.id,dep.depAmt "
+ "ORDER BY fad.id",AssetDepModel.class);
return (List<Object>)query.getResultList();
}
List<Object> objList = assetDepEJB.findByAssetIdForSaleWriteOff(faObj.getAssId());
Double amountDepTillNow = 0.0;
int fadId = 0;
int i=0;
for (Iterator<Object> iterator3 = objList.iterator(); iterator3
.hasNext();) {
Object[] obj = (Object[]) iterator3
.next();
if (i>0) {
if (fadId != (Integer) obj[0]) {
break;
}
}
fadId = (Integer) obj[0];
amountDepTillNow += (Double)obj[1];
i++;
}
It worked for me but If there is another efficient way, PLEASE DO SUGGEST ME.