JPA 2.0 insufficient privileges when performing insert from transaction - java

After much searching and trials, I am stuck... I have two classes, one is ExpectedSecurityReturn and the other is ForecastReturnType. ForecastReturnType is a member of ExpectedSecurityReturn but should not be inserted when persisting data. I keep getting an "insufficient privileges" but I know that the user does have the delete/insert privileges to the table expected_security_return since I tested with JDBC and JPA delete works fine. Therefore, I think that it has to do with my classes.
#Table(name = "EXPECTED_SECURITY_RETURNS")
#Entity
#IdClass(ExpectedSecurityReturn.ExpectedSecurityReturnPK.class)
public class ExpectedSecurityReturn {
#Id
#Column(name = "REP_SEC_ID")
private Integer repSecId;
#Id
#Column(name = "AS_OF_DATE")
private Date date;
#Id
#ManyToOne(optional = false)
#JoinColumn(name = "RETURN_TYPE_ID", referencedColumnName = "RETURN_TYPE_ID", insertable=false)
private ForecastReturnType returnType;
#Column(name="CURR_TOUSD_RET") // local currency to usd
private Double currencyToUsdReturn;
}
The primary key class, which includes ForecastReturnType:
// ------------------------------
// PK
// ------------------------------
public static class ExpectedSecurityReturnPK implements Serializable {
private static final long serialVersionUID = 1325372032981567439L;
public ExpectedSecurityReturnPK() {
}
public ExpectedSecurityReturnPK(final Integer repSecId,
final Date asOfDate, ForecastReturnType returnType) {
if (repSecId == null)
throw new IllegalArgumentException("null rep sec id");
if (asOfDate == null)
throw new IllegalArgumentException("null asOfDate");
if (returnType == null)
throw new IllegalArgumentException("null returnType");
this.repSecId = repSecId;
this.date = new Date(asOfDate.getTime());
}
#Override
public boolean equals(final Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final ExpectedSecurityReturnPK that = (ExpectedSecurityReturnPK) o;
if (repSecId != that.repSecId)
return false;
if (!date.equals(that.date))
return false;
if (!returnType.equals(that.returnType))
return false;
return true;
}
#Override
public int hashCode() {
int result = repSecId;
result = 31 * result + date.hashCode();
result = 31 * result + returnType.getForecastTypeId();
return result;
}
private int repSecId;
private Date date;
private ForecastReturnType returnType;
}
and ForecastReturnType:
#Table(name="EXPECTED_SEC_RET_TYPE_DECODE")
#Entity
public class ForecastReturnType {
#Id
#Column(name="RETURN_TYPE_ID")
private int forecastTypeId;
#Column(name="SHORT_NAME")
private String shortName;
#Column(name="LONG_NAME")
private String longName;
#OneToMany(fetch=FetchType.LAZY, mappedBy="returnType")
Collection<ExpectedSecurityReturn> expectedSecurityReturns;
}
Could anyone help me figure out what I am doing wrong? I tried many things without success... I think that the culprit is ExpectedSecurityReturn.returnType since I know that the user does not have privileges.
Basically, I need to insert/persist ExpectedSecurityReturn instances.

Well, there's a couple of things.
I would heavily not recommend even trying to do this. You can waste away your life figuring out JPA annotations and weird issues like this that never quite seem to work right. You'll also find that different JPA providers will behave slightly differently when it comes to more complex structures like this, and it goes doubly for inheritance.
You're really much better off creating a unique key on EXPECTED_SECURITY_RETURNS, and just living with it, it will make your Java life much much easier.
If you have to do something like this, I'm not surprised that JPA is balking at having a primary key component be another entity object. Whilst this in of course quite possible in the RDBMS, it's seemingly little things like this that will trip up JPA.
I would also check the query logs that your JPA impl will put out (it's configurable fairly easily in the persistence definition for most JPA providers, certainly Ecpliselink and Hibernate). I'd be willing to bet it's trying to run an update on EXPECTED_SEC_RET_TYPE_DECODE, and if not, it might be trying to obtain a lock (table, row or other depending on your DBMS). If the user doesn't have permission to either execute a lock or an update on that table, depending on the exact implementation, the query could fail with a permissions problem.
It is reasonable for JPA to want to hold a lock on that table because there is a chance that during the transaction, the entry that is being referenced in EXPECTED_SEC_RET_TYPE_DECODE may get changed, so it must ensure that it doesn't whilst updating/inserting on the other table. Last I checked, there is no way to tell JPA that this table is essentially static. If you're using Hibernate, you might try the #ReadOnly annotation, but in the past, not much I've tried can get around things like this.
If you do find a better solution, feel free to post it so that the rest of us can learn!!

I agree with PlexQ that derived identities and composite keys are pretty complicated parts of JPA.
However, JPA 2.0 specification contains a good set of examples to illustrate these topics, and these examples mostly work across different JPA implementations.
For your case specification suggests you to put into #IdClass a field with name of #ManyToOne field and type of #Id field of referenced entity:
#Entity
public class Employee {
#Id long empId;
String empName;
...
}
public class DependentId {
String name; // matches name of #Id attribute
long emp; // matches name of #Id attribute and type of Employee PK
}
#Entity
#IdClass(DependentId.class)
public class Dependent {
#Id String name;
// id attribute mapped by join column default
#Id #ManyToOne Employee emp;
...
}
See also:
JSR 317: JavaTM Persistence 2.0

After a lot of trial and error, I finally figured out that the error was legitimate and I did not indeed have sufficient (ie insert) privileges, only delete!!

Related

JPA Attribute Converter being applied despite autoApply=false and property not annotated with #Convert

The system I'm working on has a bunch of legacy data where boolean values have been stored as 'Y' and 'N'. New tables use a BIT column instead and simply store 0 and 1. No table mixes the two approaches.
To support the legacy tables we have the following converter:
#Converter(autoApply = false)
public class BooleanToStringConverter implements AttributeConverter<Boolean, String> {
private Logger.ALogger Log = Logger.of(BooleanToStringConverter.class);
#Override
public String convertToDatabaseColumn(final Boolean attribute) {
Log.debug("Converting the boolean value {}", attribute);
if (attribute == null) {
return "N";
}
return attribute ? "Y" : "N";
}
#Override
public Boolean convertToEntityAttribute(final String dbData) {
return "Y".equalsIgnoreCase(dbData);
}
}
As this only needs to apply to certain entities the autoApply property has been set to false.
I'm now creating a brand new entity, with a new table. It has two boolean properties, both using the BIT column style instead of Y/N:
#Entity
#Table(name = "MyEntity")
public class MyEntity {
#Id
#Column(name = "MyEntityId")
private Long id;
#Column(name = "IsClosed")
private Boolean closed;
...
}
Note that I have not applied the #Convert annotation.
I have a query that needs to filter out any rows where the entity is closed:
query.where().eq(CLOSED, Boolean.FALSE)
It is at this point that my problem arises. Whenever this query is run I see the log message from the BooleanToStringConverter being written to the logs and indeed, if I dump the actual SQL that was executed from the MySQL database then I can see that the converter did actually get applied to the boolean property, creating the following SQL fragment:
select <columns>
from MyEntity t0
where <other predicates>
and t0.IsClosed = 'N'
order by <order clause>
This is obviously wrong - the converter shouldn't have been applied, it's not set to be automatic and the closed property isn't annotated with #Convert.
I tried to work around this by creating a second converter:
#Converter(autoApply = true)
public class BooleanConverter implements AttributeConverter<Boolean, Boolean> {
private Logger.ALogger Log = Logger.of(BooleanConverter.class);
#Override
public Boolean convertToDatabaseColumn(final Boolean attribute) {
Log.debug("Processing the value {}.", attribute);
return attribute;
}
#Override
public Boolean convertToEntityAttribute(final Boolean dbData) {
return dbData;
}
}
This resulted in both converters being applied to the property and I see both debug statements appearing in the logs.
2019-07-29 14:19:53,994 [dispatcher-69] DEBUG BooleanConverter Processing the value false.
2019-07-29 14:19:53,994 [dispatcher-69] DEBUG BooleanToStringConve I'm Converting the boolean value false
Next I tried explicitly setting the converter to use on the entity itself (I hoped this might change the order that the converters were getting applied in so that it'd end up as true/false despite the other converter running):
#Entity
#Table(name = "MyEntity")
public class MyEntity {
#Id
#Column(name = "MyEntityId")
private Long id;
#Convert(converter = BooleanConverter.class)
#Column(name = "IsClosed")
private Boolean closed;
...
}
With exactly the same result; both converters are applied to the value sequentially, with the BooleanToStringConverter having the last laugh and mangling the predicate.
I would rather keep the BooleanToStringConverter as it makes dealing with the legacy data a bit less painful, but unless I can figure out why it's being applied when it shouldn't it's looking likely that I'll have to delete it.
I'm using Ebean version 4.1.3 and Play! 2.6.21
How can I stop this rogue converter from applying itself to properties that it has no right to be touching?
This is a (now) known limitation of Ebean, as described by Issue 1777 on Ebean's GitHub page. It is not planned to be fixed at the time of writing.

Spring Data JPA - bidirectional relation with infinite recursion

First, here are my entities.
Player :
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class,
property="id")
public class Player {
// other fields
#ManyToOne
#JoinColumn(name = "pla_fk_n_teamId")
private Team team;
// methods
}
Team :
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class,
property="id")
public class Team {
// other fields
#OneToMany(mappedBy = "team")
private List<Player> members;
// methods
}
As many topics already stated, you can avoid the StackOverflowExeption in your WebService in many ways with Jackson.
That's cool and all but JPA still constructs an entity with infinite recursion to another entity before the serialization. This is just ugly ans the request takes much longer. Check this screenshot : IntelliJ debugger
Is there a way to fix it ? Knowing that I want different results depending on the endpoint. Examples :
endpoint /teams/{id} => Team={id..., members=[Player={id..., team=null}]}
endpoint /members/{id} => Player={id..., team={id..., members=null}}
Thank you!
EDIT : maybe the question isn't very clear giving the answers I get so I'll try to be more precise.
I know that it is possible to prevent the infinite recursion either with Jackson (#JSONIgnore, #JsonManagedReference/#JSONBackReference etc.) or by doing some mapping into DTO. The problem I still see is this : both of the above are post-query processing. The object that Spring JPA returns will still be (for example) a Team, containing a list of players, containing a team, containing a list of players, etc. etc.
I would like to know if there is a way to tell JPA or the repository (or anything) to not bind entities within entities over and over again?
Here is how I handle this problem in my projects.
I used the concept of data transfer objects, implemented in two version: a full object and a light object.
I define a object containing the referenced entities as List as Dto (data transfer object that only holds serializable values) and I define a object without the referenced entities as Info.
A Info object only hold information about the very entity itself and not about relations.
Now when I deliver a Dto object over a REST API, I simply put Info objects for the references.
Let's assume I deliever a PlayerDto over GET /players/1:
public class PlayerDto{
private String playerName;
private String playercountry;
private TeamInfo;
}
Whereas the TeamInfo object looks like
public class TeamInfo {
private String teamName;
private String teamColor;
}
compared to a TeamDto
public class TeamDto{
private String teamName;
private String teamColor;
private List<PlayerInfo> players;
}
This avoids an endless serialization and also makes a logical end for your rest resources as other wise you should be able to GET /player/1/team/player/1/team
Additionally, the concept clearly separates the data layer from the client layer (in this case the REST API), as you don't pass the actually entity object to the interface. For this, you convert the actual entity inside your service layer to a Dto or Info. I use http://modelmapper.org/ for this, as it's super easy (one short method call).
Also I fetch all referenced entities lazily. My service method which gets the entity and converts it to the Dto there for runs inside of a transaction scope, which is good practice anyway.
Lazy fetching
To tell JPA to fetch a entity lazily, simply modify your relationship annotation by defining the fetch type. The default value for this is fetch = FetchType.EAGER which in your situation is problematic. That is why you should change it to fetch = FetchType.LAZY
public class TeamEntity {
#OneToMany(mappedBy = "team",fetch = FetchType.LAZY)
private List<PlayerEntity> members;
}
Likewise the Player
public class PlayerEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "pla_fk_n_teamId")
private TeamEntity team;
}
When calling your repository method from your service layer, it is important, that this is happening within a #Transactional scope, otherwise, you won't be able to get the lazily referenced entity. Which would look like this:
#Transactional(readOnly = true)
public TeamDto getTeamByName(String teamName){
TeamEntity entity= teamRepository.getTeamByName(teamName);
return modelMapper.map(entity,TeamDto.class);
}
In my case I realized I did not need a bidirectional (One To Many-Many To One) relationship.
This fixed my issue:
// Team Class:
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Player> members = new HashSet<Player>();
// Player Class - These three lines removed:
// #ManyToOne
// #JoinColumn(name = "pla_fk_n_teamId")
// private Team team;
Project Lombok might also produce this issue. Try adding #ToString and #EqualsAndHashCode if you are using Lombok.
#Data
#Entity
#EqualsAndHashCode(exclude = { "members"}) // This,
#ToString(exclude = { "members"}) // and this
public class Team implements Serializable {
// ...
This is a nice guide on infinite recursion annotations https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
You can use #JsonIgnoreProperties annotation to avoid infinite loop, like this:
#JsonIgnoreProperties("members")
private Team team;
or like this:
#JsonIgnoreProperties("team")
private List<Player> members;
or both.

JPA #GeneratedValue and #Id

In my Entity there's a field called id, marked with the annoation #Id (which I set manually) and know I wanted to know, is there a possibility to auto generate a Unique Id (with #GeneratedValue) but I also want to set the ID manually and when not set it should generate it auotmatically.
Example:
#Id
#GeneratedValue
private long id;
creation of the entity
new Entity(someid) //Once with ID and no generated value
new Entity() //Second without ID and generated unique value
I hope you can understand me.
Well I don't think that this would work. JPA automatically generates your id value and is responsible for the #GeneratedValue. Ask yourself 'what happens if there is already an existing entity with the id 100. And I create manually a new entity with tne id 100'. Intuitively I would say that JPA (or the implementation) throws an expection.
While writing the above answer I got the idea of writing your own generator (not tried at all, just coded down here at stackoverflow.
You can pass your own implementation of a generator to the #GeneratedValue annotation
#Id
#GeneratedValue(startegy = GenerationType.IDENTITY, generator="generatedIdOrCustomId")
#GenericGenerator(name="generatedIdOrCustomId", strategy="GeneratedIdOrCustomId")
private Long id;
...
And the custom implementation should look like this:
public class GeneratedIdOrCustomId extends IdentityGenerator {
#Override
public Serializable generate(SessionImplementor session, Object obj) throws HibernateException {
if (((YourEntity) obj).getId() == null) {
// the id is null, let JPA create an id.
return super.generate(session, obj);
} else {
// the id is set and should not be generated by JPA.
return ((YourEntity) obj).getId();
}
}
The generate method is just a quick and dirty implementation. You'll have to check for example also if the obj == null and throw a (Hibernate)Exception in this case.

How to know the ID (#GeneratedValue) of a POJO at runtime

I have a form to fill a POJO called Father. Inside it, I have a FotoFather field.
When I save a new Father, I save automatically the object FotoFather (with Hibernate ORM pattern).
FotoFather.fotoNaturalUrl must be filled with the value of Father.id and here is the problem!
When i'm saving Father on the db, of course I still haven't Father.id value to fill FotoFather.fotoNaturalUrl. How can I solve this problem?
Thank you
#Entity
#Table(name = "father")
public class Father implements Serializable{
...
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
...
#OneToOne(targetEntity = FotoFather.class, fetch = FetchType.EAGER)
#JoinColumn(name = "fotoFather", referencedColumnName = "id")
#Cascade(CascadeType.ALL)
private FotoFather fotoFather;
}
FotoFather.class
#Entity
#Table(name = "foto_father")
public class FotoFather.class{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
...
#Column(name = "foto_natural_url")
private String fotoNaturalUrl;
...
}
If you simply need the complete URL for some application-specific purpose, I would likely err on the side of not trying to store the URL with the ID at all and instead rely on a transient method.
public class FotoFather {
#Transient
public String getNaturalUrl() {
if(fotoNaturalUrl != null && fotoNaturalUrl.trim().length > 0) {
return String.format("%s?id=%d", fotoNaturalUrl, id);
}
return "";
}
}
In fact, decomposing your URLs even more into their minimalist variable components and only storing those in separate columns can go along way in technical debt, particularly if the URL changes. This way the base URL could be application-configurable and the variable aspects that control the final URL endpoint are all you store.
But if you must know the ID ahead of time (or as in a recent case of mine, keep identifiers sequential without loosing a single value), you need to approach this where FotoFather identifiers are generated prior to persisting the entity, thus they are not #GeneratedValues.
In order to avoid issues with collisions at insertion, we have a sequence service class that exposes support for fetching the next sequence value by name. The sequence table row is locked at read and updated at commit time. This prevents multiple sessions from concurrency issues with the same sequence, prevents gaps in the range and allows for knowing identifiers ahead of time.
#Transactional
public void save(Father father) {
Assert.isNotNull(father, "Father cannot be null.");
Assert.isNotNull(father.getFotoFather(), "FotoFather cannot be null.");
if(father.getFotoFather().getId() == null) {
// joins existing transaction or errors if one doesn't exist
// when sequenceService is invoked.
Long id = sequenceService.getNextSequence("FOTOFATHER");
// updates the fotofather's id
father.getFotoFather().setId(id);
}
// save.
fatherRepository.save(father);
}
I think you can do be registering an #PostPersist callback on your Father class. As the JPA spec notes:
The PostPersist and PostRemove callback methods are invoked for an
entity after the entity has been made persistent or removed. These
callbacks will also be invoked on all entities to which these
operations are cascaded. The PostPersist and PostRemove methods will
be invoked after the database insert and delete operations
respectively. These database operations may occur directly after the
persist, merge, or remove operations have been invoked or they may
occur directly after a flush operation has occurred (which may be at
the end of the transaction). Generated primary key values are
available in the PostPersist method.
So, the callback should be called immediately after the Father instance is written to the database and before the FotoFather instance is written.
public class Father(){
#PostPersist
public void updateFotoFather(){
fotofather.setNaturalUrl("/xyz/" + id);
}
}

How annotation mapping is done in java persistence?

We use annotations for mapping the entity class with the database table by simply specifying #Entity and more like #Id, table joins and many things. I do not know how these entity variables are getting mapped with database table. Can anyone give a short description for understanding.
Thanks :)
Well the idea is to translate your objects and their connections with other objects into a relational database. These two ways of representing data (objects defined by classes and in tables in a database) are not directly compatible and that is where a so called Object Relational Mapper framework comes into play.
So a class like
class MyObject
{
private String name;
private int age;
private String password;
// Getters and setters
}
Will translate into a database table containing a column name which is of type varchar, age of type int and password of type varchar.
Annotations in Java simply add additional information (so called meta data) to your class definitions, which can be read by any other class (e.g. JavaDoc) and in the case of the Java Persistence API will be used by an ORM framework like Hibernate to read additional information you need to translate your object into the database (your database table needs a primary id and some information - like what type of a relation an object has to another - can't be automatically determined by just looking at your class definition).
Annotations are very well explained here:
http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/
annotations are just metadata on a class, nothing magical. You can write your own annotations. Those annotations are given retention policies of runtime (which means you have access to that metadata at runtime). When you call persist etc the persistence provider iterates through the fields (java.lang.reflect.Field) in your class and checks what annotations are present to build up your SQL statement. Try writing your own annotation and doing something with it. It won't seem very magical after that.
in your case annotation working means mapping with tablename with entity class is look like as ....
#Entity
#Table(name = "CompanyUser")
public class CompanyUserCAB implements java.io.Serializable
{
private long companyUserID;
private int companyID;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "companyUserID")
public long getCompanyUserID()
{
return this.companyUserID;
}
public void setCompanyUserID(long companyUserID)
{
this.companyUserID = companyUserID;
}
#Column(name = "companyID")
public int getCompanyID()
{
return this.companyID;
}
public void setCompanyID(int companyID)
{
this.companyID = companyID;
}
}

Categories

Resources