This is my first post here, I've been searching for a long time here but I didn't found a problem that seemed similar.
When I use JpaRepository function findOne(id) for one of my classes, it returns null. As if no row had been found for this id.
Of course the database row with this id exists.
Also my class mapping seems right.
I don't understand because I already used findOne() for other classes and I never had any problem.
Anyone can tell me what can be the source of this problem, please ? That would be nice !
This is my DAO :
#Transactional
public interface OrderDetailDAO extends JpaRepository<OrderDetail, Integer>
{
}
This is my Model :
#Entity
#Table(name = "order_detail", schema = "", catalog = AppConfig.databaseSchema)
public class OrderDetail implements Serializable {
private int idOrderDetail;
private Order order;
private Preorder preorder;
private UnitType unitType;
private Sale sale;
private DeliveryStatusType deliveryStatusType;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_Order_Detail")
public int getIdOrderDetail() {
return idOrderDetail;
}
public void setIdOrderDetail(int idOrderDetail) {
this.idOrderDetail = idOrderDetail;
}
#ManyToOne
#JoinColumn(name = "id_Order", referencedColumnName = "id_Order", nullable = false)
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
#ManyToOne
#JoinColumn(name = "id_Preorder", referencedColumnName = "id_Preorder", nullable = false)
public Preorder getPreorder() {
return preorder;
}
public void setPreorder(Preorder preorder) {
this.preorder = preorder;
}
#ManyToOne
#JoinColumn(name = "id_Unit_Type", referencedColumnName = "id_Unit_Type")
public UnitType getUnitType() {
return unitType;
}
public void setUnitType(UnitType unitType) {
this.unitType = unitType;
}
#ManyToOne
#JoinColumn(name = "id_Sale", referencedColumnName = "id_Sale")
public Sale getSale() {
return sale;
}
public void setSale(Sale sale) {
this.sale = sale;
}
#ManyToOne
#JoinColumn(name = "id_Delivery_Status_Type", referencedColumnName = "id_Delivery_Status_Type")
public DeliveryStatusType getDeliveryStatusType() {
return deliveryStatusType;
}
public void setDeliveryStatusType(DeliveryStatusType deliveryStatusType) {
this.deliveryStatusType = deliveryStatusType;
}
}
When I write a request manually, like this :
#Query("SELECT o FROM OrderDetail o WHERE o.idOrderDetail = :idOrderDetail")
public OrderDetail findOneCustom(#Param("idOrderDetail") Integer idOrderDetail);
That works, but that's ugly so I would prefer to use JpaRepository native function findOne()
After all investigation, I have found an interesting answer that is worked for me. I think it is all about defining column type on Db. For my case, I have defined the variable (rid as column) as varchar2(18) that was RID CHAR(18 BYTE).
Java part:
if (dhFlightRepo.findOneFlight(dhFlight.getRid())== null) {
dhFlightRepo.save(dhFlight);
}
If your value that you used as a parameter for findOne() is smallest than set value on column (18 for my case),the jpa doesn't accept value and returns null.You have to change column type as varchar2(18) it can be changeable according to given value on findOne() and work perfect.
I hope that works for all of you.I kindly request to give more detail If someone knows the reason with more detail.
Related
Well... I have an auto-generated database by hibernate and when I try to call findAll() in controller I receive that
More than one row with the given identifier was found: 1, for class: com.example.rentacar.domain.Masina
Anyway, I checked database I don t have duplicates keys. This is the controller:
#Controller
public class MasinaController {
MasinaService masinaService;
#Autowired
public MasinaController (MasinaService masinaService){
this.masinaService = masinaService;
}
#RequestMapping("/masini")
public String getMasini(Model model){
var masini = masinaService.findMasini();
model.addAttribute("masini", masini);
return "masini";
}
}
service:
#Autowired
public MasinaServiceImpl(MasinaRepo masinaRepo) {
this.masinaRepo = masinaRepo;
}
public List<Masina> findMasini(){
var masini = masinaRepo.findAll();
return masini;
}
this is the domain:
#Entity
#Data
public class Masina {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String model;
private Integer capacitateCilindrica;
private Integer putere;
private Integer anProductie;
private String culoare;
private String numarInmatriculare;
private Float pret;
private Boolean esteInchiriata;
#OneToOne(mappedBy = "masina")
private ChirieActiva ChirieActiva;
#OneToMany(mappedBy = "masina")
private List<ChirieFinalizata> ChiriiFinalizate;
#OneToOne
private Firma Firma;
#ManyToMany(mappedBy = "Masini",
cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Categorie> CategoriiMasina;
}
The rest of object that are linked are empty(no-entries).
I tried to see what identifier is corrupted so I iterate through all entries to see what id will return me an error. After full iteration none of the entries returned error..
The funniest part is that if I add this code in controller (iterate through all entries from db using findById before calling findAll) it WORKS !!!!
So.. my controller looks like this...
#RequestMapping("/masini")
public String getMasini(Model model){
for(int i=1; i<9;i++){
var masina = masinaService.findById(i);
}
var masini = masinaService.findMasini();
model.addAttribute("masini", masini);
return "masini";
}
Did anyone know why that happens? Thanks a lot!
I solved the problem by adding (fetch = FetchType.LAZY) to #OneToOne relationships.
As the title may be a bit vague, here is an explanation of my problem (which is not easy to summarize into a concise title).
My problem is quite simple (what I'm trying to achieve seems really intuitive to me), though solving it might not be. I've two abstract classes representing tied concepts. As an example I choose Project and Task. A given project can have many tasks. These classes are abstract because there are different kind of projects and different kind of tasks, but projects and tasks kinds are the same. For example there are development project and development tasks (may seems a bit odd but this is a simplified example). Of course there are other kinds of projects and tasks but let's stick to these ones.
What I want to achieve is to map, with JPA, my relationship in the two abstract classes and benefit from it in the concrete classes. What complicates things is that I must also map inheritance: single table with discriminator, which is also part of classes ids. I add generic types to abstract classes to ensure that I can force concrete classes to refer to the right associated classes (DevelopmentProject tied to DevelopmentTask).
Problem is, whatever I try, I always get an error of some kind. What I seek here is somebody that've achieved something like this. I must tell you that I'm working with a legacy database (I'm well aware that current design suffer from many problems, but there is unfortunately nothing I can do for it).
To help you, I join the example code I made, that way you will see what I meant in the above paragraphs.
#Entity
#Table(name = "PROJECT")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Project<T extends Task> {
/* Fields */
private ProjectId id;
private List<T> tasks;
/* Constructors */
protected Project() {}
public Project(ProjectId id) {
this.id = id;
}
/* Getters */
#EmbeddedId
public ProjectId getId() {
return id;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "project", targetEntity = Task.class)
public List<T> getTasks() {
return tasks;
}
/* Setters */
public void setTasks(List<T> tasks) {
this.tasks = tasks;
}
}
#Embeddable
public class ProjectId {
/* Fields */
private Integer type;
private Integer number;
/* Constructors */
#SuppressWarnings("unused")
private ProjectId() {} /* Empty constructor for hibernate */
public ProjectId(Integer type, Integer number) {
this.type = type;
this.number = number;
}
/* Getters */
#Column(name = "TYPE", nullable = false)
private Integer getType() { /* Only for Hibernate */
return type;
}
#Column(name = "NUMBER", nullable = false)
public Integer getNumber() {
return number;
}
/* Setters */
#SuppressWarnings("unused")
private void setType(Integer type) { /* Only for Hibernate */
this.type = type;
}
public void setNumber(Integer number) {
this.number = number;
}
}
#Entity
#Table(name = "TASK")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Task<P extends Project> {
/* Fields */
private TaskId id;
private P project;
/* Constructors */
protected Task() {}
public Task(TaskId id) {
this.id = id;
}
/* Getters */
#EmbeddedId
public TaskId getId() {
return id;
}
#ManyToOne(fetch = FetchType.EAGER, optional = false, targetEntity = Project.class)
#JoinColumns({
#JoinColumn(name = "TYPE", referencedColumnName = "TYPE", insertable = false, updatable = false),
#JoinColumn(name = "PROJECT_NUMBER", referencedColumnName = "NUMBER", insertable = false, updatable = false)
})
public P getProject() {
return project;
}
/* Setters */
public void setId(TaskId id) {
this.id = id;
}
public void setProject(P project) {
this.project = project;
}
}
#Embeddable
public class TaskId {
/* Fields */
private Integer type;
private Integer projectNumber;
private Integer number;
/* Constructors */
#SuppressWarnings("unused")
private TaskId() {} /* Empty constructor for Hibernate */
public TaskId(Integer type, Integer projectNumber, Integer number) {
this.type = type;
this.projectNumber = projectNumber;
this.number = number;
}
/* Getters */
#Column(name = "TYPE", nullable = false)
private Integer getType() { /* Only for Hibernate */
return type;
}
#Column(name = "PROJECT_NUMBER", nullable = false)
public Integer getProjectNumber() {
return projectNumber;
}
#Column(name = "NUMBER", nullable = false)
public Integer getNumber() {
return number;
}
/* Setters */
#SuppressWarnings("unused")
private void setType(Integer type) { /* Only for Hibernate */
this.type = type;
}
public void setProjectNumber(Integer projectNumber) {
this.projectNumber = projectNumber;
}
public void setNumber(Integer number) {
this.number = number;
}
}
#Entity
#DiscriminatorValue(value = "1")
public class DevelopmentProject extends Project<DevelopmentTask> {
/* Constructors */
#SuppressWarnings("unused")
private DevelopmentProject() { /* Empty constructor for Hibernate */
super();
}
public DevelopmentProject(DevelopmentProjectId id) {
super(id);
}
}
public class DevelopmentProjectId extends ProjectId {
/* Constructors */
public DevelopmentProjectId(Integer number) {
super(1, number); /* Development type is forced */
}
}
#Entity
#DiscriminatorValue(value = "1")
public class DevelopmentTask extends Task<DevelopmentProject> {
/* Constructors */
#SuppressWarnings("unused")
private DevelopmentTask() { /* Empty constructor for Hibernate */
super();
}
public DevelopmentTask(DevelopmentTaskId id) {
super(id);
}
}
public class DevelopmentTaskId extends TaskId {
/* Constructors */
public DevelopmentTaskId(Integer number) {
super(1, number); /* Development type is forced */
}
}
EDIT :
I corrected my example a bit (adding a projectNumber to Task, without it the relationship seems meanless, and even erroneous). I also added targetEntity on my relationships (I forgot it and you will see why below).
Now I will try to explain you my path until here.
First of all I used MappedSuperclass annotation on my superclasses and no targetEntity on my relationships. That lead me to an error because of the generics: Hibernate didn't know what to map and suggest me to either remove generics, either use targetEntity, either use #Type (but that last one being Hibernate specific I prefer not to use it). So I set up targetEntity with Project.class and Task.class (at the superclasses level there were nothing else to do it seems).
Then, I ran into another problem because of MappedSuperclass annotation. Hibernate tells me there is a reference to an unknown entity Project (or Task) [I'm adapting from there errors I got in my real project but I'm pretty sure I'll have the same once I'll manage to set those two tables up and tests them]. I googled that error and finally found that MappedSuperclass was the problem and that I could use Entity on abstract classes. So did I.
After that, and that's the last error I got : repeated column mapping for column TYPE in DevelopmentProject. But If I try to set insertable = false and updatable = false everywhere this column is defined (id, relationship)... that doesn't change anything. I fear to have fallen in the same case of my precedent question : How to map to attributes from embedded class to a single database column with JPA?. But in that case, there was an easy and not-too-ugly workaround. In this case I can't see it.
And I really wonder if someone have ever tried to achieve something like this and succeed, even if the solution is different, as it's as handy and elegant as possible.
I have created some Hibernate mappings with Hibernate 4.3.8.
#Entity
#Table(name = ErrorEntity.TABLE_ID)
#XmlRootElement(name = ErrorEntity.XML_ROOT_TAG)
public class ErrorEntity {
/**
*
*/
private static final long serialVersionUID = 8083918635458543738L;
public static final String TABLE_ID = "Error";
public static final String ERRORCODE = "error_code";
public static final String ENV_ID = "envid";
private Integer error_code;
private Integer envId;
private EnvironmentEntity environment;
public ErrorEntity() {
}
#Id
#Column(name = ErrorEntity.ERRORCODE)
public Integer getError_code() {
return error_code;
}
public void setError_code(Integer errorcode) {
this.error_code = errorcode;
}
#Column(name = ErrorEntity.ENV_ID)
public Integer getEnvId() {
return envId;
}
public void setEnvId(Integer envId) {
this.envId = envId;
}
#ManyToOne
#JoinColumn(name = ErrorEntity.ENV_ID, referencedColumnName = EnvironmentEntity.ENV_ID, insertable = false, updatable = false)
public EnvironmentEntity getEnvironment() {
return environment;
}
public void setEnvironment(EnvironmentEntity environment) {
this.environment = environment;
}
}
As you can see the mapping property ENV_ID is mapped twice.
Thisway I thought I would be able to set the JoinColumn value without querying the database to get the mapped object because I have the JoinColumn value at this point.
The value of ENV_ID is written correctly to the database but if I query this ErrorEntity later and try to get the EnvironmentEntity the reference is null.
ErrorEntity error = (ErrorEntity) criteria.uniqueResult();
System.out.println(error.getEnvironment().getName());
getEnvironment() returns null.
Any ideas how to achieve this?
Edit
It was working like expected to create a new object with the PK set.
Now I have a special situation where it does not work.
I need to reference another object where the joincolumn is not the PK. I know that the value i will join on is unique but there are also some duplicate values i will not join on.
However Hibernate seems to be unable to map this relationship automatically.
ErrorEntity error = new ErrorEntity();
SignalEntity signal = new SignalEntity();
signal.setName(signalName);
error.setSignal(signal);
The problem is that I do not have the signalID (PK) in that situation. The other idea would be to query the db but thats too slow.
I tried to create an composite PK with 3 columns but this breaks the logic at another place.
Is it possible to create two independent PK's?
The ErrorEntity has two ErrorEntity.ENV_ID mappings, which unless you use #MapsId then it's a configuration issue.
You should have an env_id column in EnvironmentEntity table and just the:
#ManyToOne
#JoinColumn(name = ErrorEntity.ENV_ID, referencedColumnName = EnvironmentEntity.ENV_ID, insertable = false, updatable = false)
public EnvironmentEntity getEnvironment() {
return environment;
}
mapping in ErrorEntity.
My suggestion is to remove this:
#Column(name = ErrorEntity.ENV_ID)
public Integer getEnvId() {
return envId;
}
To set the envId directly without querying the database and request the whole EnvironmentEntity, you can do something like this:
errrorEntity.setEnvironment(new EnvironmentEntity());
errrorEntity.getEnvironment().setEnvId(envId);
This is not a JPA standard requirement but Hibernate supports it.
Having a bit of bother trying to figure out how to get my #ManyToMany mapping working in Hibernate in Dropwizard (using dropwizard-hibernate 6.2). I've tried several of the online examples. I'm trying to persist a twitter stream with user_mentions saved in a Targets table which is m2m with the Tweets table. So far all my attempts have been with an existing Target and a new Tweet (and due to my business rules, that will always be the case). I'll show code momentarily, but the consistent problem I'm having is that that the tweets_targets table winds up in all cases with the target_id set to the correct value, but the tweet_id set to 0.
Code is based on an article here: http://viralpatel.net/blogs/hibernate-many-to-many-annotation-mapping-tutorial/
// Target class
#Entity
#Table(name="targets")
public class Target {
private long id;
private List<Tweet> tweets = new ArrayList<Tweet>();
#Id
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#ManyToMany(mappedBy="targets",targetEntity=Tweet.class)
public List<Tweet> getTweets() {
return tweets;
}
public void setTweets(List<Tweet> tweets) {
this.tweets = tweets;
}
}
// Tweet class
#Entity
#Table(name="tweets")
public class Tweet {
private long id;
private List<Target> targets = new ArrayList<Target>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "targets_tweets", joinColumns = {
#JoinColumn(name = "tweet_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "target_id", nullable = false, updatable = false)
})
public List<Target> getTargets() {
return this.targets;
}
public void setTargets(List<Target> targets) {
this.targets = targets;
for(Target t: targets){
t.getTweets().add(this);
}
}
}
The actual saving of a new Tweet is done in the DAO class which inherits from AbstractDAO in DropWizard. Relevant code is:
public long create(Tweet tweet) {
tweet.setTargets(getTargets(tweet));
return persist(tweet).getId();
}
#SuppressWarnings("unchecked")
private List<Target> getTargets(Tweet tweet) {
String[] mentions = tweet.getUserMentions().split(",");
return namedQuery(Target.FIND_BY_HANDLE)
.setParameterList("handles", mentions).list();
}
My named query just returns a list of all my targets based on their twitter handle as reported by the streams API.
Found the answer, hopefully this will help someone else.
The Id's in my DB are autoincrementing (I know, there's all kinds of debate on that, but it's what I have to work with), so once I added the annotation #GeneratedValue(strategy=GenerationType.IDENTITY) to the Tweet's Id property, everything started working.
My Hibernate-JPA domain model has these entities:
AttributeType ------< AttributeValue
The relevant Java classes look like this (getters and setters omitted):
#Entity
public class AttributeType {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(unique = true, nullable = false)
private String name;
#OneToMany(mappedBy = "attributeType", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private List<AttributeValue> values = new ArrayList<AttributeValue>();
}
#Entity #Table(uniqueConstraints = #UniqueConstraint(columnNames = {"value", "attribute_type_id"}))
public class AttributeValue {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(optional = false)
private AttributeType attributeType;
#Column(nullable = false)
private String value;
}
Notice there's a unique constraint on AttributeValue.value and AttributeValue.attributeType, because for an attribute type (e.g. size) we don't want to allow an attribute value (e.g. small) to occur more than once.
If I update an AttributeType by performing the following operations within a single transaction:
delete "small" attribute value from "size" attribute type
add "small" attribute value to "size" attribute type
I get an exception that indicates the unique constraint was violated. This suggests that Hibernate-JPA is performing the insertion of the attribute value before the delete, which seems to invite this kind of problem for no obvious reason.
The class that performs the update of an AttributeType looks like this:
#Transactional(propagation = Propagation.SUPPORTS)
public class SomeService {
private EntityManager entityManager; // set by dependency injection
#Transactional(propagation = Propagation.REQUIRED)
public AttributeType updateAttributeType(AttributeType attributeType) throws Exception {
attributeType = entityManager.merge(attributeType);
entityManager.flush();
entityManager.refresh(attributeType);
return attributeType;
}
}
I could workaround this problem by iterating over the attribute values, figuring out which ones have been updated/deleted/inserted, and performing them in this order instead:
deletes
updates
inserts
But it seems like the ORM should be able to do this for me. I've read that Oracle provides a "deferConstraints" option that causes constraints to be checked only when a transaction has completed. However, I'm using SQL Server, so this won't help me.
You need to use a composite ID instead of a generated ID.
HHH-2801
The problem arises when a new association entity with a generated ID
is added to the collection. The first step, when merging an entity
containing this collection, is to cascade save the new association
entity. The cascade must occur before other changes to the collection.
Because the unique key for this new association entity is the same as
an entity that is already persisted, a ConstraintViolationException is
thrown. This is expected behavior.
Using a new collection (i.e., one-shot delete), as suggested in the
previous comment) also results in a constraint violation, since the
new association entity will be saved on the cascade of the new
collection.
An example of one of the approaches (using a composite ID instead of a generated ID) is illustrated >in manytomanywithassocclass.tar.gz and is checked into Svn.
#Entity
public class AttributeType {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
#Column(unique = true, nullable = false)
private String name;
#OneToMany(mappedBy = "attributeType", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private List<AttributeValue> values = new ArrayList<AttributeValue>();
//Getter, Setter...
}
#Entity
#Table (uniqueConstraints = #UniqueConstraint(columnNames = { "value", "attributeType_id" }))
public class AttributeValue{
#EmbeddedId AttributeValueId id;
#MapsId(value= "id")
#ManyToOne(optional = false)
private AttributeType attributeType;
private String value2;
public AttributeValue() {
this.id = new AttributeValueId();
}
public AttributeType getAttributeType() {
return attributeType;
}
public void setAttributeType(AttributeType pAttributeType) {
this.id.setAttributeTypeID(pAttributeType.getId());
this.attributeType = pAttributeType;
}
public String getValue() {
return id.getAttributeValue();
}
public void setValue(String value) {
this.id.setAttributeValue(value);
}
#Embeddable
public static class AttributeValueId implements Serializable {
private Integer id;
private String value;
public AttributeValueId() {
}
public AttributeValueId(Integer pAttributeTypeID, String pAttributeValue) {
this.id = pAttributeTypeID;
this.value = pAttributeValue;
}
public Integer getAttributeTypeID() {
return id;
}
public void setAttributeTypeID(Integer attributeTypeID) {
this.id = attributeTypeID;
}
public String getAttributeValue() {
return value;
}
public void setAttributeValue(String attributeValue) {
this.value = attributeValue;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime
* result
+ ((id == null) ? 0 : id
.hashCode());
result = prime
* result
+ ((value == null) ? 0 : value.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AttributeValueId other = (AttributeValueId) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
}
}
See 5.1.2.1. Composite identifier on how to do it with JPA annotation.
See Chapter 8. Component Mapping
See 8.4. Components as composite identifiers
I am not sure if I understand the question as it is getting late, but first thing I would try would be to override AttributeValue's equals method to contain those two unique fields.
In hibernate session there is one queue for the delete and one for the insert. Debug to see if deletes comes before insert.
Look at the merge. Try using update instead.