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.
Related
In our spring boot application, I am trying to save an aggregate, that consists of a root entity (ParentEntity) and a Set of child entities (ChildEntity).
The intention is, that all operations are done through the aggreate. So there is no need for a repository for ChildEntity, as the ParentEntity is supposed to manage all save or update operations.
This is how the Entities look like:
#Entity
#Table(name = "tab_parent", schema = "test")
public class ParentEntity implements Serializable {
#Id
#Column(name = "parent_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer parentId;
#Column(name = "description")
private String description;
#Column(name = "created_datetime", updatable = false, nullable = false)
#ColumnTransformer(write = "COALESCE(?,CURRENT_TIMESTAMP)")
private OffsetDateTime created;
#Column(name = "last_modified_datetime", nullable = false)
#ColumnTransformer(write = "COALESCE(CURRENT_TIMESTAMP,?)")
private OffsetDateTime modified;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "ParentEntity")
private Set<ChildEntity> children;
// constructor and other getters and setters
public void setChildren(final Set<ChildEntity> children) {
this.children = new HashSet<>(children.size());
for (final ChildEntity child : children) {
this.addChild(child);
}
}
public ParentEntity addChild(final ChildEntity child) {
this.children.add(child);
child.setParent(this);
return this;
}
public ParentEntity removeChild(final ChildEntity child) {
this.children.add(child);
child.setParent(null);
return this;
}
}
#Entity
#DynamicUpdate
#Table(name = "tab_child", schema = "test")
public class ChildEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "child_id")
private Integer childId;
#Column(name = "language_id")
private String languageId;
#Column(name = "text")
private String text;
#Column(name = "created_datetime", updatable = false, nullable = false)
#ColumnTransformer(write = "COALESCE(?,CURRENT_TIMESTAMP)")
public OffsetDateTime created;
#Column(name = "last_modified_datetime", nullable = false)
#ColumnTransformer(write = "COALESCE(CURRENT_TIMESTAMP,?)")
public OffsetDateTime modified;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "parent_id", updatable = false)
private ParentEntity parent;
// constructor and other getters and setters
public ParentEntity getParent() {
return this.parent;
}
public void setParent(final ParentEntity parent) {
this.parent = parent;
}
}
This is the store method to save or update the entities:
public Integer merge(final ParentDomainObject parentDomainObject) {
final ParentEntity parentEntity =
this.mapper.toParentEntity(parentDomainObject);
final ParentEntity result = this.entityManager.merge(parentEntity);
this.entityManager.flush();
return result.getParentId();
}
And this is the store method to retrieve the aggregate by id:
public Optional<ParentDomainObject> findById(final Integer id) {
return this.repo.findById(id).map(this.mapper::toParentDomainObject);
}
As you can see our architecture strictly separates the store from the service layer. So the service only knows about domain objects and does not depend on Hibernate Entites at all.
When updating either the child or the parent, firstly the parent is loaded. In the service layer, the domain object is updated (fields are set, or a child is added/removed).
Then the merge method (see code snippet) of the store is called with the updated domain object.
This works, but not completely as we want to. Currently every update leads to the parent and EVERY chhild entity being saved, even if all field remained the same. We added the #DynamicUpdate annotaton. Now we saw, that the "modified" field is the problem.
We use a #ColumnTransformer to have the database set the date. Now even if you call the services update method without changing anything, Hibernate generates a update query for EVERY object, which updates only the modified field.
The worst thing about that is, as every object is saved, every modified date changed as well to the current date. But we need information about exactly which object really changed and when.
Is there any way to tell hibernate, that this column should not be taken into account when deciding what to update. However of course, if a field changed, the update operation should indeed update the modified field.
UPDATE:
My second approach after #Christian Beikov mentioned the use of #org.hibernate.annotations.Generated( GenerationTime.ALWAYS )
is the following:
Instead of #Generated (which uses #ValueGenerationType( generatedBy = GeneratedValueGeneration.class )),
I created my own annotations, which use custom AnnotationValueGeneration implementations:
#ValueGenerationType(generatedBy = CreatedTimestampGeneration.class)
#Retention(RetentionPolicy.RUNTIME)
public #interface InDbCreatedTimestamp {
}
public class CreatedTimestampGeneration
implements AnnotationValueGeneration<InDbCreatedTimestamp> {
#Override
public void initialize(final InDbCreatedTimestamp annotation, final Class<?> propertyType) {
}
#Override
public GenerationTiming getGenerationTiming() {
return GenerationTiming.INSERT;
}
#Override
public ValueGenerator<?> getValueGenerator() {
return null;
}
#Override
public boolean referenceColumnInSql() {
return true;
}
#Override
public String getDatabaseGeneratedReferencedColumnValue() {
return "current_timestamp";
}
}
#ValueGenerationType(generatedBy = ModifiedTimestampGeneration.class)
#Retention(RetentionPolicy.RUNTIME)
public #interface InDbModifiedTimestamp {
}
public class ModifiedTimestampGeneration
implements AnnotationValueGeneration<InDbModifiedTimestamp> {
#Override
public void initialize(final InDbModifiedTimestamp annotation, final Class<?> propertyType) {
}
#Override
public GenerationTiming getGenerationTiming() {
return GenerationTiming.ALWAYS;
}
#Override
public ValueGenerator<?> getValueGenerator() {
return null;
}
#Override
public boolean referenceColumnInSql() {
return true;
}
#Override
public String getDatabaseGeneratedReferencedColumnValue() {
return "current_timestamp";
}
}
I use these annotations in my entities instead of the #ColumnTransformer annotations now.
This works flawlessly when I insert a new ChildEntity via addChild(), as now not all timestamps of all entities of the aggregate are updated anymore. Only the timestamps of the new child are set now.
In other words, the InDbCreatedTimestamp works as it should.
Sadly, the InDbModifiedTimestamp does not. Because of GenerationTiming.ALWAYS, I expected the timestamp to be generated on db level, everytime an INSERT OR UPDATE is issued. If I change a field of a ChildEntity and then save the aggregate, an update statement is generated only for this one database row, as expected. However, the last_modified_datetime column is not updated, which is surprising.
It seems that this is unfortunately still an open bug. This issue describes my problem precisely: Link
Can someone provide a solution how to get this db function executed on update as well (without using db triggers)
You could try to use #org.hibernate.annotations.Generated( GenerationTime.ALWAYS ) on these fields and use a database trigger or default expression to create the value. This way, Hibernate will never write the field, but read it after insert/update.
Overall this has a few downsides though (need the trigger, need a select after insert/update), so I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO/domain model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(ParentEntity.class)
#UpdatableEntityView
public interface ParentDomainObject {
#IdMapping
Integer getParentId();
OffsetDateTime getModified();
void setModified(OffsetDateTime modified);
String getDescription();
void setDescription(String description);
Set<ChildDomainObject> getChildren();
#PreUpdate
default preUpdate() {
setModified(OffsetDateTime.now());
}
#EntityView(ChildEntity.class)
#UpdatableEntityView
interface ChildDomainObject {
#IdMapping
Integer getChildId();
String getName();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ParentDomainObject a = entityViewManager.find(entityManager, ParentDomainObject.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<ParentDomainObject> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary! It also supports writing/mapping back to the persistence model in an efficient manner. Since it does dirty tracking for you, it will only flush changes if the object is actually dirty.
public Integer merge(final ParentDomainObject parentDomainObject) {
this.entityViewManager.save(this.entityManager, parentDomainObject);
this.entityManager.flush();
return parentDomainObject.getParentId();
}
I have two entities and a service. Without #Transactional everything worked fine (except rollback). Now I added a #Transactional to the service method to make it an transaction and rollback automatically on errors. But now all tests using this method fail with javax.persistence.EntityNotFoundException: Unable to find org.kitodo.mediaserver.core.db.entities.Work with id xyz (xyz is the ID of my Work item).
Then I tried to add cascade = {CascadeType.PERSIST, CascadeType.MERGE} to the work field of ActionData entity. Than I get another exception on the same position as before: org.h2.jdbc.JdbcSQLException: Concurrent update in table "WORK": another transaction has updated or deleted the same row [90131-196]
I assume for some reason it tries to use two transitions at the same time.
What's the reason and how can I make this work?
The entities
#Entity
public class Work {
private String id;
private String title;
private String path;
private String hostId;
private Instant indexTime;
private Set<Collection> collections;
private String allowedNetwork = "global";
protected Work() {}
public Work(String id, String title) {
this.id = id;
this.title = title;
}
#Id
public String getId() {
return id;
}
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "work_collection",
joinColumns = #JoinColumn(name = "work_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "collection_name", referencedColumnName = "name"))
public Set<Collection> getCollections() {
return collections;
}
// getters/setters
}
#Entity
public class ActionData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ElementCollection
#CollectionTable(name = "action_parameter")
private Map<String, String> parameter = new HashMap<>();
#ManyToOne
#JoinColumn(name = "work_id", referencedColumnName = "id", nullable = false)
private Work work;
private String actionName;
private Instant requestTime;
private Instant startTime;
private Instant endTime;
private ActionData() {}
public ActionData(Work work, String actionName, Map<String, String> parameter) {
this.work = work;
this.parameter = parameter;
this.actionName = actionName;
}
// getters/setters
}
The Service method
#Service
public class ActionService {
#Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public Object performRequested(ActionData actionData) throws Exception {
// some checks
actionData.setStartTime(Instant.now());
// !!! javax.persistence.EntityNotFoundException: Unable to find org.kitodo.mediaserver.core.db.entities.Work with id xyz
actionRepository.save(actionData);
IAction actionInstance = getActionInstance(actionData.getActionName());
Object result;
result = actionInstance.perform(actionData.getWork(), actionData.getParameter());
actionData.setEndTime(Instant.now());
actionRepository.save(actionData);
return result;
}
}
The test
#Test
public void performRequestedAction() throws Exception {
// given
init();
work1 = entityManager.persist(work1);
actionData1 = new ActionData(work1, "mockAction", parameter1);
actionData1.setRequestTime(Instant.now());
actionData1 = entityManager.persist(actionData1);
entityManager.flush();
// when
Object action = actionService.performRequested(actionData1);
// then
assertThat(action).isNotNull();
assertThat(action).isInstanceOf(String.class);
assertThat(action).isEqualTo("performed");
assertThat(actionData1.getStartTime()).isBetween(Instant.now().minusSeconds(2), Instant.now());
assertThat(actionData1.getEndTime()).isBetween(Instant.now().minusSeconds(2), Instant.now());
}
I suspect, your Unittest is not working in autocommit mode.
The problem might be, that you don't commit the inserting-transaction in your testing function.
Therefore the saved data can not be seen by the called method actionService.performRequested() which starts a completely new transaction. This transaction will not be allowed to see any dirty data.
So either make sure that the data is saved either by setting autocommit-mode or committing the transaction which persists actionData1 in performRequestedAction.
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.
Im currently working with Hibernate and Java, im trying to persist objects in our database. We have a Group class and a ToDoList class. Group has a one-to-many relationship with ToDoList as show in this ERD.
The relevant code from the Group class:
#Entity
#Table(name = "Group", catalog = "db")
public class Group implements java.io.Serializable{
private int id;
private Set<ToDoList> allToDoLists;
public Group(){
allToDoLists = new HashSet<ToDoList>(0);
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "Group_id", unique = true, nullable = false)
public int getId(){
return this.id;
}
public void setId(int id){
this.id = id;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "group")
public Set<ToDoList> getAllToDoLists() {
return this.allToDoLists;
}
public void addTodoList(ToDoList t){
this.allToDoLists.add(t);
t.setGroup(this);
}
public void setAllToDoLists(Set<ToDoList> allToDoLists) {
this.allToDoLists = allToDoLists;
}
}
The relevant code from the ToDoList class:
#Entity
#Table (name = "Todo_List", catalog = "db")
public class ToDoList {
private int id;
private Group group;
public ToDoList(){}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "Todo_List_id", unique = true, nullable = false)
public int getId(){
return this.id;
}
public void setId(int id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Group", nullable = false)
public Group getGroup() {
return this.group;
}
public void setGroup(Group group) {
this.group = group;
}
}
The code where the problem occurs:
ToDoList toDoList = new ToDoList("TodoList1","Iets","24-06-2015","24-06-2015");
Group group = new Group("Groep 1","10-11-2011","10-11-2011");
GroupDao groupDao = new GroupDaoImpl();
groupDao.store(group);
toDoList.setGroup(group);
toDoListDao.store(toDoList);
The code in 'groupDao' and 'toDoListDao' for storing are basicly the same:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
session.save(toDoList);
session.getTransaction().commit();
Problem
When I try to store the ToDoList I get the following error:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Group, Name) values ('2015-06-24', '2015-06-24', 'Iets', 23, 'TodoList1')' at line 1
Needed
It whould be best if I could store Group and ToDoList whould be save also.
Given our coming deadline it whould also suffice if I can store Group and ToDoList on their own
"Group" is an SQL keyword ("group by"). As such it is not the best choice of table name. It looks like this is causing conflicts given that Hibernate is generating SQL not accepted by the DBMS.
If you can't change the table name, you can probably solve that conflict by quoting the name in your entity, like so:
#Table(name = "`Group`", catalog = "db")
or
#Table(name = "\"Group\"", catalog = "db")
EDIT:
And as ug_ informs me, you can also set the following hibernate property in your persistence.xml file to do that by default to all identifiers:
<property name="hibernate.globally_quoted_identifiers" value="true" />
I have never used that myself, but the Javadocs confirm that this is indeed a supported property:
https://docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/cfg/AvailableSettings.html
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.