I have the next two entities
Person:
#Entity
#Table(name = "person")
public class PersonDTO implements Serializable {
private static final long serialVersionUID = -3859029259805663330L;
#Id
#Column(name = "person_id")
#SequenceGenerator(name = "PERSON_GENERATOR", sequenceName = "seq_person_id")
#GeneratedValue(generator = "PERSON_GENERATOR")
private Long personId;
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "person_id", updatable = false)
#Cascade(value = { CascadeType.ALL, CascadeType.DELETE_ORPHAN })
private List<PersonBookDTO> personBooks = new ArrayList<PersonBookDTO>();
public Long getPersonId() {
return personId;
}
public void setPersonId(final Long personId) {
this.personId = personId;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public List<PersonBookDTO> getPersonBooks() {
return personBooks;
}
public void setPersonBooks(final List<PersonBookDTO> personBooks) {
this.personBooks = personBooks;
}
/**
* #see java.lang.Object#equals(Object)
*/
#Override
public boolean equals(final Object object) {
if (!(object instanceof PersonDTO)) {
return false;
}
PersonDTO rhs = (PersonDTO) object;
return new EqualsBuilder().appendSuper(super.equals(object))
.append(this.name, rhs.name).isEquals();
}
/**
* #see java.lang.Object#hashCode()
*/
#Override
public int hashCode() {
return new HashCodeBuilder(1636021877, -141724713)
.appendSuper(super.hashCode())
.append(this.name).toHashCode();
}
}
Person book:
#Entity
#Table(name = "person_book")
#NamedQueries({
#NamedQuery(name = "PersonBookDTO.getBooksByPersonIdList", query = "from PersonBookDTO s where s.person.personId in(:personIdList) and s.disabled=false")
})
public class PersonBookDTO implements Serializable {
private static final long serialVersionUID = -6382678873261874993L;
#Id
#Column(name = "person_book_id")
#SequenceGenerator(name = "PERSON_BOOK_GENERATOR", sequenceName = "seq_person_book_id")
#GeneratedValue(generator = "PERSON_BOOK_GENERATOR")
private Long personBookId;
#Column(name = "name")
private String name;
#Column(name = "disabled")
private boolean disabled;
#ManyToOne()
#JoinColumn(name = "person_id")
private PersonDTO person;
public Long getPersonBookId() {
return personBookId;
}
public void setPersonBookId(Long personBookId) {
this.personBookId = personBookId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isDisabled() {
return disabled;
}
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
public PersonDTO getPerson() {
return person;
}
public void setPerson(PersonDTO person) {
this.person = person;
}
/**
* #see java.lang.Object#equals(Object)
*/
public boolean equals(Object object) {
if (!(object instanceof PersonBookDTO)) {
return false;
}
PersonBookDTO rhs = (PersonBookDTO) object;
return new EqualsBuilder().appendSuper(super.equals(object)).append(this.disabled, rhs.disabled).append(this.personBookId, rhs.personBookId).append(this.name, rhs.name).append(this.person, rhs.person).isEquals();
}
/**
* #see java.lang.Object#hashCode()
*/
public int hashCode() {
return new HashCodeBuilder(213186089, -1592457573).appendSuper(super.hashCode()).append(this.disabled).append(this.personBookId).append(this.name).append(this.person).toHashCode();
}
}
I've enabled hibernate 2nd level cache for this entities:
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</property>
<property name="net.sf.ehcache.configurationResourceName">/ehcache.xml</property>
...
<class-cache usage="read-write" class="com.test.dao.dto.PersonDTO"/>
<class-cache usage="read-write" class="com.test.dao.dto.PersonBookDTO"/>
Default cache from ehcache.xml:
<defaultCache
maxElementsInMemory="100000"
eternal="false"
timeToIdleSeconds="86400"
timeToLiveSeconds="86400"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"
statistics="false"
>
Now I call the next test method in loop several times and in several threads:
(personIdList is a List and was filled previously)
txManager.startTransaction();
Query query = getSession().getNamedQuery("PersonBookDTO.getBooksByPersonIdList");
query.setParameterList("personIdList", personIdList);
List<PersonBookDTO> = query.list();
txManager.commitTransaction();
txManager.closeTransaction();
When I collected hibernate 2nd level cache statistis I've seen that only PersonDTO was taken from cache during the loop.
name: com.test.dao.dto.PersonDTO
count: 13
hit: 4
miss: 4
put: 13
size: 35389
and PersonBookDTO was only written
name: com.test.dao.dto.PersonBookDTO
count: 29
hit: 0
miss: 0
put: 29
size: 38194
I've enabled TRACE level for ehCache. Here's a part of log:
TRACE [ReadWriteCache] Caching: com.test.dao.dto.PersonBookDTO#8348946
TRACE [ReadWriteCache] Cached: com.test.dao.dto.PersonBookDTO#8348946
TRACE [ReadWriteCache] Cache hit: com.test.dao.dto.PersonDTO#70276
TRACE [ReadWriteCache] Cache hit: com.test.dao.dto.PersonDTO#79271
TRACE [ReadWriteCache] Caching: com.test.dao.dto.PersonBookDTO#8376615
TRACE [ReadWriteCache] Item was already cached: com.test.dao.dto.PersonBookDTO#8376615
TRACE [ReadWriteCache] Cache hit: com.test.dao.dto.PersonDTO#63179
TRACE [ReadWriteCache] Caching: com.test.dao.dto.PersonBookDTO#8315141
TRACE [ReadWriteCache] Item was already cached: com.test.dao.dto.PersonBookDTO#8315141
Maybe someone can explain this behavoir. Thanks!
UPDATE: this reproduces with all queries which returns Lists of DTOs. session.read(PersonBookDTO) reads from cache normally.
Results of explicit query are fetched from the second level cache only when query cache is turned on and enabled for that query (by setting appropriate query hints). So, if you want to cache results of the query, you need to enable query cache.
If you want to retrieve entity data from the second level cache, but don't want to retrieve query result as a whole from the query cache, you can run a query for ids and then fetch entities with those ids manually.
Related
I have a model :
public class ABC implements Serializable {
private int baseId;
private Integer aId;
private Integer bId;
private Boolean isReal;
private TimeStamp updateTime;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "base_id", nullable = false)
public int getBaseId() {
return baseId;
}
public void setBaseId(int baseId) {
this.baseId = baseId;
}
#Basic
#Column(name = "a_id", nullable = false)
public Integer getAId() {
return aId;
}
public void setAId(Integer aId) {
this.aId = aId;
}
#Basic
#Column(name = "b_id", nullable = false)
public Integer getBId() {
return bId;
}
public void setBId(Integer bId) {
this.bId = bId;
}
#Basic
#Column(name = "is_real")
public Boolean getIsReal() {
return isReal;
}
public void setIsReal(Boolean isReal) {
this.isReal = isReal;
}
#Basic
#Column(name ="update_time")
public Timestamp getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Timestamp updateTime) {
this.updateTime = updateTime;
}
}
I have a controller Class:
#RestController
#RequestMapping(path = "${serverconfig.api-base-path}/base")
public class BaseController {
/**
* Instance of an logger
*/
private static final Logger LOG =
LoggerFactory.getLogger(BaseController.class);
/**
* Base repository
*/
private BaseRepository baseRepository;
/***
*
* #param baseRepository
*/
public BaseController(BaseRepository baseRepository) {
LOG.trace("BaseRepository constructor method.");
this.baseRepository = baseRepository;
}
#PostMapping(path = Route.UPDATE_IS_REAL)
// #Transactional
public ABC updateIsReal(#Valid #RequestBody
#RequestParam("baseId") int baseId,
#RequestParam("isReal") boolean isReal){
ABC abc = baseRepository.findByBaseId(baseId);
Date date= new Date();
Timestamp ts = new Timestamp(date.getTime());
abc.setBaseId(baseId);
abc.setIsReal(isReal);
abc.setUpdateTime(ts);
return baseRepository.save(abc);
}
}
My repository class:
#Repository
public interface BaseRepository extends
JpaRepository<ABC, Integer> {
List<ABC> findByAId(Integer aId);
ABC findByBaseId(Integer baseId);
}
Database table has an entry :
"base_id": 1,
"a_Id": 1,
"b_Id": 1,
"is_real": null,
"update_time": null
When I call the endpoint it gives no error and returns:
"base_id": 1,
"aId": 1,
"bId": 1,
"isReal": yes,
"updateTime": 018-10-01T18:30:56.765+0000
But When I query the database, the record is not updated there. I am not understanding what I am doing wrong. I am supplying id when I try to make a rest call and that id exists in the database.
With save, changes won't necessary be flushed to DB immediately and might stay just in memory, until flush or commit commands are issued.
With saveAndFlush, changes will be flushed to DB immediately.
However, if you flush the changes in transaction and do not commit them, the changes still won't be visible to the outside transactions until the commit in this transaction.
In your BaseController try changing
return baseRepository.save(abc);
to
return baseRepository.saveAndFlush(abc);
Further information here and here
If someone was here for the same reason and didn't find an answer..:
Did you check if you have following set? Perhaps the whole schema may be getting re-created?
spring.jpa.hibernate.ddl-auto=create
I'm having issues trying to delete an entry in my database table.
The structure of the database is as follows:
User ---< UserET >--- ExitTicket
ExitTicket.java
#Entity
#Table(name = "exit_ticket")
public class ExitTicketEntry implements Serializable {
private static final long serialVersionUID = 1L;
private long id;
private String title;
private String dateET;
private Set<UserET> userETs= new HashSet<UserET>();
public ExitTicketEntry() {}
public ExitTicketEntry(String title,String dateET) {
this.title = title;
this.dateET = dateET;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY )
#Column(name = "ticket_id")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#OneToMany(mappedBy = "exitTicketEntry", orphanRemoval=true)
public Set<UserET> getUserETs() {
return userETs;
}
public void setUserETs(Set<UserET> userETs) {
this.userETs = userETs;
}
public void addUserETs(UserET userETs) {
this.userETs.add(userETs);
}
//getters and setters
}
UserET.java
#Entity
#Table(name="userET")
public class UserET implements Serializable {
//private static final long serialVersionUID = 1L;
private long answerId;
private User user;
private ExitTicketEntry exitTicketEntry;
private String answer;
private Date dateAnswer;
#Id
#GeneratedValue
#Column(name="answerId")
public long getAnswerId() {
return answerId;
}
public void setAnswerId(long answerId) {
this.answerId = answerId;
}
#ManyToOne(cascade = CascadeType.ALL )
#JoinColumn(name = "user_id")
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "ticket_id")
public ExitTicketEntry getExitTicketEntry() {
return exitTicketEntry;
}
public void setExitTicketEntry(ExitTicketEntry exitTicketEntry) {
this.exitTicketEntry = exitTicketEntry;
}
#Column(name = "answer")
public String getAnswer(){
return answer;
}
public void setAnswer(String answer) {
this.answer = answer;
}
#Column(name = "dateAnswer")
#Temporal(TemporalType.DATE)
public Date getDateAnswer() {
return dateAnswer;
}
public void setDateAnswer(Date dateAnswer) {
this.dateAnswer = dateAnswer;
}
User.java
#Entity
#Table(name="USER")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private long id;
private String user_name, user_password, user_type,user_realname;
private Set<UserET> userETs= new HashSet<UserET>();
public User() {
}
public User(String user_name,String user_password,String user_type,String user_realname) {
this.user_name = user_name;
this.user_password = user_password;
this.user_type = user_type;
this.user_realname=user_realname;
}
public void addExitTicketEntry(UserET group) {
this.userETs.add(group);
}
#Id
#GeneratedValue
#Column(name="user_id")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#OneToMany(mappedBy = "user", orphanRemoval=true)
public Set<UserET> getUserETs() {
return userETs;
}
public void setUserETs(Set<UserET> userETs) {
this.userETs = userETs;
}
public void addUserETs(UserET userETs) {
this.userETs.add(userETs);
} ... + getters and setters
I am able to insert a new Entry of ExitTicket correctly (using ExitTicketService.java), with this function:
public void addEntryET(ExitTicketEntry exitTicketEntry) {
log.info("adding entry in database");
try {
hibernateTemplate.save(exitTicketEntry);
}
catch(Exception e) {
log.info("error adding an entry in the database->"+e.toString());
}
}
However when I try to delete an entry from the database table, the hibernate delete only does 'select' commands and never gets to do a 'delete'
2018-11-05 09:28:27 INFO AuthService:72 - entro 1 ------
Hibernate: select exitticket0_.ticket_id as ticket_i1_0_0_, exitticket0_.dateET as dateET2_0_0_, exitticket0_.title as title3_0_0_ from exit_ticket exitticket0_ where exitticket0_.ticket_id=?
Mon Nov 05 09:28:27 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
2018-11-05 09:28:27 TRACE BasicBinder:65 - binding parameter [1] as [BIGINT] - [9]
2018-11-05 09:28:27 TRACE BasicExtractor:61 - extracted value ([dateET2_0_0_] : [VARCHAR]) - [2011-05-20]
2018-11-05 09:28:27 TRACE BasicExtractor:61 - extracted value ([title3_0_0_] : [VARCHAR]) - [test1]
2018-11-05 09:28:27 TRACE CollectionType:790 - Created collection wrapper: [com.jcg.spring.hibernate.pojo.ExitTicketEntry.userETs#9]
Hibernate: select userets0_.ticket_id as ticket_i4_2_0_, userets0_.answerId as answerId1_2_0_, userets0_.answerId as answerId1_2_1_, userets0_.answer as answer2_2_1_, userets0_.dateAnswer as dateAnsw3_2_1_, userets0_.ticket_id as ticket_i4_2_1_, userets0_.user_id as user_id5_2_1_, user1_.user_id as user_id1_1_2_, user1_.user_name as user_nam2_1_2_, user1_.user_password as user_pas3_1_2_, user1_.user_realname as user_rea4_1_2_, user1_.user_type as user_typ5_1_2_ from userET userets0_ left outer join USER user1_ on userets0_.user_id=user1_.user_id where userets0_.ticket_id=?
2018-11-05 09:28:27 TRACE BasicBinder:65 - binding parameter [1] as [BIGINT] - [9]
2018-11-05 09:28:27 INFO AuthService:99 - deleted entry with ID#=9
My delete function in my service class:
public void deleteById(Class<?> type, long id) {
log.info("entro 1 ------");
ExitTicketEntry e=(ExitTicketEntry) hibernateTemplate.load(type,id);
hibernateTemplate.delete(e);
}
I've been reading plenty of forums, but still have the same issue, I can't delete any entries. Am I missing something?
I'm currently working on a project where I'm trying to get a list of enities from table which does not have a primary key (dk_systemtherapie_merkmale). This table is 1:n related to another table (dk_systemtherapie). See the screenshot for the table structure.
When getting an entry for dk_systemtherapie, the program fetches the Collection "dkSystemtherapieMerkmalesById". However, the first table entry is fetched as often as the number of actual entries in the table is. It never fetches the other entries from dk_systemtherapie_merkmale. I assume it has something to do with the fact that hibernate can't differ between the entries, but I don't know how to fix it.
Table schema
I've created two corresponding entity classes, dk_systemtherapie:
#Entity
#Table(name = "dk_systemtherapie", schema = "***", catalog = "")
public class DkSystemtherapieEntity {
private int id;
private Collection<DkSystemtherapieMerkmaleEntity> dkSystemtherapieMerkmalesById;
#Id
#Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#OneToMany(mappedBy = "dkSystemtherapieByEintragId")
public Collection<DkSystemtherapieMerkmaleEntity> getDkSystemtherapieMerkmalesById() {
return dkSystemtherapieMerkmalesById;
}
public void setDkSystemtherapieMerkmalesById(Collection<DkSystemtherapieMerkmaleEntity> dkSystemtherapieMerkmalesById) {
this.dkSystemtherapieMerkmalesById = dkSystemtherapieMerkmalesById;
}
}
Here the second one, which is accessing the table without a primary key, dk_systhemtherapie_merkmale:
#Entity #IdClass(DkSystemtherapieMerkmaleEntity.class)
#Table(name = "dk_systemtherapie_merkmale", schema = "***", catalog = "")
public class DkSystemtherapieMerkmaleEntity implements Serializable {
#Id private Integer eintragId;
#Id private String feldname;
#Id private String feldwert;
private DkSystemtherapieEntity dkSystemtherapieByEintragId;
#Basic
#Column(name = "eintrag_id")
public Integer getEintragId() {
return eintragId;
}
public void setEintragId(Integer eintragId) {
this.eintragId = eintragId;
}
#Basic
#Column(name = "feldname")
public String getFeldname() {
return feldname;
}
public void setFeldname(String feldname) {
this.feldname = feldname;
}
#Basic
#Column(name = "feldwert")
public String getFeldwert() {
return feldwert;
}
public void setFeldwert(String feldwert) {
this.feldwert = feldwert;
}
#Id
#ManyToOne
#JoinColumn(name = "eintrag_id", referencedColumnName = "id")
public DkSystemtherapieEntity getDkSystemtherapieByEintragId() {
return dkSystemtherapieByEintragId;
}
public void setDkSystemtherapieByEintragId(DkSystemtherapieEntity dkSystemtherapieByEintragId) {
this.dkSystemtherapieByEintragId = dkSystemtherapieByEintragId;
}
}
I assume the problem is releated to the fact that Hibernate is using the following annotation as the one and only id for fetching data from database.
#Id
#ManyToOne
#JoinColumn(name = "eintrag_id", referencedColumnName = "id")
public DkSystemtherapieEntity getDkSystemtherapieByEintragId() {
return dkSystemtherapieByEintragId;
}
This leads to the problem that when getting more than one entry with the same id (as the id is not unique), you will get the number of entries you would like to but hibernate is always fetching the first entry for this id. So in fact you are getting dublicate entries.
So how to fix this?
According to this question: Hibernate and no PK, there are two workarounds which are actually only working when you don't have NULL entries in your table (otherwise the returning object will be NULL as well) and no 1:n relationship. For my understanding, hibernate is not supporting entities on tables without primary key (documentation). To make sure getting the correct results, I would suggest using NativeQuery.
Remove the Annotations and private DkSystemtherapieEntity dkSystemtherapieByEintragId; (incl. beans) from DkSystemtherapieMerkmaleEntity.java und add a constructor.
public class DkSystemtherapieMerkmaleEntity {
private Integer eintragId;
private String feldname;
private String feldwert;
public DkSystemtherapieMerkmaleEntity(Integer eintragId, String feldname, String feldwert) {
this.eintragId = eintragId;
this.feldname = feldname;
this.feldwert = feldwert;
}
public Integer getEintragId() {
return eintragId;
}
public void setEintragId(Integer eintragId) {
this.eintragId = eintragId;
}
public String getFeldname() {
return feldname;
}
public void setFeldname(String feldname) {
this.feldname = feldname;
}
public String getFeldwert() {
return feldwert;
}
public void setFeldwert(String feldwert) {
this.feldwert = feldwert;
}
}
Remove private Collection<DkSystemtherapieMerkmaleEntity> dkSystemtherapieMerkmalesById; (incl. beans) from DkSystemtherapieEntity.java.
Always when you need to get entries for a particular eintrag_id, use the following method instead of the Collection in DkSystemtherapieEntity.java.
public List<DkSystemtherapieMerkmaleEntity> getDkSystemtherapieMerkmaleEntities(int id) {
Transaction tx = session.beginTransaction();
String sql = "SELECT * FROM dk_systemtherapie_merkmale WHERE eintrag_id =:id";
List<Object[]> resultList;
resultList = session.createNativeQuery(sql)
.addScalar("eintrag_id", IntegerType.INSTANCE)
.addScalar("feldname", StringType.INSTANCE)
.addScalar("feldwert", StringType.INSTANCE)
.setParameter("id", id).getResultList();
tx.commit();
List<DkSystemtherapieMerkmaleEntity> merkmale = new ArrayList<>();
for (Object[] o : resultList) {
merkmale.add(new DkSystemtherapieMerkmaleEntity((Integer) o[0], (String) o[1], (String) o[2]));
}
return merkmale;
}
Call getDkSystemtherapieMerkmaleEntities(dkSystemtherapieEntityObject.getid()) instead of getDkSystemtherapieMerkmalesById().
I'm working with Spring, hibernate and MySql but I have some problem with seralization of query result.
First in my entity I added #JsonManagedReference on Set structure (#OneToMany side) and #JsonBackReference on single object reference (#ManyToOne side) and it works but I wasn't be able to retrieve all needed information (for example #ManyToOne reference).
So i swapping #JsonBackReference on set structure and #JsonManagedReference on single object but I retrieve
No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: com.model.tablesField.TableUI["data"]->java.util.ArrayList[0]->com.domain.Car["carType"]->com.domain.CarType_$$_jvst744_f["handler"])
I tried also with #JsonIgnore on Set structure but it doesn't work for the same issues.
This is my spring configuration
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
// properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
properties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
properties.put("hibernate.enable_lazy_load_no_trans",true);
return properties;
and this is part of one of my several entities:
/**
* Car generated by hbm2java
*/
#Entity
#Table(name = "car", catalog = "ATS")
public class Car implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Integer idCar;
#JsonManagedReference
private CarType carType;
#JsonManagedReference
private Fleet fleet;
private String id;
private int initialKm;
private String carChassis;
private String note;
#JsonBackReference
private Set<Acquisition> acquisitions = new HashSet<Acquisition>(0);
public Car() {
}
public Car(CarType carType, Fleet fleet, int initialKm, String carChassis) {
this.carType = carType;
this.fleet = fleet;
this.initialKm = initialKm;
this.carChassis = carChassis;
}
public Car(CarType carType, Fleet fleet, String id, int initialKm, String carChassis, String note,
Set<Acquisition> acquisitions) {
this.carType = carType;
this.fleet = fleet;
this.id = id;
this.initialKm = initialKm;
this.carChassis = carChassis;
this.note = note;
this.acquisitions = acquisitions;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id_car", unique = true, nullable = false)
public Integer getIdCar() {
return this.idCar;
}
public void setIdCar(Integer idCar) {
this.idCar = idCar;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_carType", nullable = false)
public CarType getCarType() {
return this.carType;
}
public void setCarType(CarType carType) {
this.carType = carType;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_fleet", nullable = false)
public Fleet getFleet() {
return this.fleet;
}
public void setFleet(Fleet fleet) {
this.fleet = fleet;
}
#Column(name = "id", length = 5)
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
#Column(name = "initialKm", nullable = false)
public int getInitialKm() {
return this.initialKm;
}
public void setInitialKm(int initialKm) {
this.initialKm = initialKm;
}
#Column(name = "carChassis", nullable = false, length = 20)
public String getCarChassis() {
return this.carChassis;
}
public void setCarChassis(String carChassis) {
this.carChassis = carChassis;
}
#Column(name = "note", length = 100)
public String getNote() {
return this.note;
}
public void setNote(String note) {
this.note = note;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "car")
public Set<Acquisition> getAcquisitions() {
return this.acquisitions;
}
public void setAcquisitions(Set<Acquisition> acquisitions) {
this.acquisitions = acquisitions;
}
}
one method that uses the query:
#Override
#RequestMapping(value = { "/cars/{idFleet}"}, method = RequestMethod.GET)
public #ResponseBody TableUI getCars(#PathVariable int idFleet) {
TableUI ajaxCall=new TableUI();
try {
ajaxCall.setData(fleetAndCarService.findCarsByIdFleet(idFleet));
return ajaxCall;
} catch (QueryException e) {
ErrorResponse errorResponse= ErrorResponseBuilder.buildErrorResponse(e);
LOG.error("Threw exception in FleetAndCarControllerImpl::addCar :" + errorResponse.getStacktrace());
return ajaxCall;
}
}
two class for the query:
public interface DefRdiRepository extends JpaRepository<DefRdi, Integer>{
//#Query("SELECT CASE WHEN COUNT(c) > 0 THEN true ELSE false END FROM DefRdi c WHERE c.parName = ?1 AND c.description= ?2")
//Boolean existsByParNameAndDescription(String parName, String description);
//Query method of spring, I put findBy and then the key of research
DefRdi findByParNameAndDescription(String parName, String description);
}
public interface CarRepository extends JpaRepository<Car, Integer>, CarRepositoryCustom {
//Query method of spring, I put findBy and then the key of research
List<Car> findByFleetIdFleet(int idFleet);
}
Where is my error? I don't want Set object but only the single reference. The problem is only when I serialize. Thanks
UPDATE:
I use #JSonIgnore on all set collectionts and Eager instead lazy ad all works fine, but is there a way to retrieve all the information only when I want, for example having two different query?
So it doesn't work
#Override
#Transactional
public List<Car> findByFleetIdFleet(int idFleet) {
List<Car> carList= carRepository.findByFleetIdFleet(idFleet);
for (Car car:carList){
Hibernate.initialize(car.getCarType());
Hibernate.initialize(car.getFleet());
}
return carList;
// return carRepository.findByFleetIdFleet(idFleet);
}
All collections need to be fetched eagerly when loading them from data base, in order to get serialized by Spring. Make sure you fetch them eagerly (e.g. FetchMode.JOIN). You could also swap #JsonManagedReference from wanted fields with #JsonIgnore to black listed fields, Spring automatically serialises every field without annotation.
Update:
Changing the data repository to something like that should work, I am not sure it compiles, but I think you will get the point:
#EntityGraph(value = "some.entity.graph", type = EntityGraph.EntityGraphType.FETCH)
#Query(
value = "SELECT c FROM Car c INNER JOIN FETCH c.acquisitions WHERE c.id = :idFleet"
)
public interface CarRepository extends JpaRepository<Car, Integer>, CarRepositoryCustom {
//Query method of spring, I put findBy and then the key of research
List<Car> findByFleetIdFleet(int idFleet);
}
For more information look at this post and read the official documentation.
Workaround:
There seems to be a workaround, however fetching those collections eager like shown above should have a positive performance impact, since there is no need for loading proxies afterwards. Also no open transactions are needed at controller level.
I'm getting this errors when trying to create relation between 2 entities, this time i'm doing this in different way - passing JSON with 2 object into helper class and then getting those object and persisting them, one by one and setting the relation. When i remove setters of relation : 1. newPerson.setKoordynator(koordynatorzyPraktykEntity);
2.koordynatorzyPraktykEntity.setKoordynatorByIdOsoby(newPerson);
then it is persisting both entities without a problem, with setters only first one (KoordynatorzyPraktykEntity) is persisted (idKoordynatora = 1, idOsoby =0, test = test )
Here is the important part of error from POSTMAN ( full log http://pastebin.com/SRmnPMBH )
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: praktyki.core.entities.KoordynatorzyPraktykEntity; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: praktyki.core.entities.KoordynatorzyPraktykEntity
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
javax.servlet.http.HttpServlet.service(HttpServlet.java:644)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
KoordynatorzyEntity:
#Entity
#Table(name = "koordynatorzy_praktyk", schema = "public", catalog = "praktykidb")
public class KoordynatorzyPraktykEntity {
private int idKoordynatoraPraktyk;
private int idOsoby;
private String doTestow;
private OsobyEntity koordynatorByIdOsoby;
private Collection<KoordynatorzyKierunkowiEntity> koordynatorzyByIdKierunku;
#Id
#GeneratedValue
#Column(name = "id_koordynatora_praktyk")
public int getIdKoordynatoraPraktyk() {
return idKoordynatoraPraktyk;
}
public void setIdKoordynatoraPraktyk(int idKoordynatoraPraktyk) {
this.idKoordynatoraPraktyk = idKoordynatoraPraktyk;
}
#Basic
#Column(name = "id_osoby")
public int getIdOsoby() {
return idOsoby;
}
public void setIdOsoby(int idOsoby) {
this.idOsoby = idOsoby;
}
/*
STUFF
*/
#JsonIgnore
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id_osoby", referencedColumnName = "id_osoby", insertable = false , updatable = false)
public OsobyEntity getKoordynatorByIdOsoby() {
return koordynatorByIdOsoby;
}
public void setKoordynatorByIdOsoby(OsobyEntity koordynatorByIdOsoby) {
this.koordynatorByIdOsoby = koordynatorByIdOsoby;
}
#JsonIgnore
#OneToMany(mappedBy = "koordynatorzyByIdKierunku", cascade = CascadeType.ALL)
#LazyCollection(LazyCollectionOption.FALSE)
public Collection<KoordynatorzyKierunkowiEntity> getKoordynatorzyByIdKierunku() {
return koordynatorzyByIdKierunku;
}
public void setKoordynatorzyByIdKierunku(Collection<KoordynatorzyKierunkowiEntity> koordynatorzyByIdKierunku) {
this.koordynatorzyByIdKierunku = koordynatorzyByIdKierunku;
}
OsobyEntity:
#Entity
#Table(name = "osoby", schema = "public", catalog = "praktykidb")
public class OsobyEntity {
private int idOsoby;
private String tytulZawodowy;
private String imie;
private String nazwisko;
private String email;
private String telefonKomorkowy;
private String telefonStacjonarny;
private KoordynatorzyPraktykEntity koordynator;
#Id
#GeneratedValue
#Column(name = "id_osoby")
public int getIdOsoby() {
return idOsoby;
}
public void setIdOsoby(int idOsoby) {
this.idOsoby = idOsoby;
}
/*
STUFF
*/
#OneToOne(mappedBy = "koordynatorByIdOsoby", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public KoordynatorzyPraktykEntity getKoordynator() {
return koordynator;
}
public void setKoordynator(KoordynatorzyPraktykEntity koordynator) {
this.koordynator = koordynator;
}
KoordynatorzyPraktykService :
public class KoordynatorzyPraktykService implements iKoordynatorzyPraktykService {
#Autowired
private iKoordynatorzyPraktykDAO ikoordynatorzyPraktykDAO;
#Autowired
private iOsobyDAO iosobyDAO;
#Override
public KoordynatorzyPraktykEntity addCoordinator(KoordynatorzyPraktykEntity koordynatorzyPraktykEntity) {
return ikoordynatorzyPraktykDAO.addCoordinator(koordynatorzyPraktykEntity);
}
/*
STUFF
*/
#Override
public OsobyEntity addPerson(OsobyEntity osobyEntity, KoordynatorzyPraktykEntity koordynatorzyPraktykEntity) {
OsobyEntity newPerson = iosobyDAO.addPerson(osobyEntity);
newPerson.setKoordynator(koordynatorzyPraktykEntity);
System.out.println(koordynatorzyPraktykEntity.toString()); //shows idKoordynatora: 1 idOsoby: 0 test: test
System.out.println(newPerson.toString()); //shows idOsoby: 32768 imie: Tomasz nazwisko: Potempa
int idOsoby = newPerson.getIdOsoby();
koordynatorzyPraktykEntity.setIdOsoby(idOsoby);
System.out.println(koordynatorzyPraktykEntity.toString()); //shows idKoordynatora: 1 idOsoby: 32768 test: test
koordynatorzyPraktykEntity.setKoordynatorByIdOsoby(newPerson);
return newPerson;
}
Both DAOs have em.persist(entity)
and POST of KoordynatorzyPraktykController:
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<KoordynatorzyPraktykEntity> addCoordinator(#RequestBody Koordynator newCoordinator) {
KoordynatorzyPraktykEntity addCoordinator = ikoordynatorzyPraktykService.addCoordinator(newCoordinator.getKoordynator());
OsobyEntity addPerson = ikoordynatorzyPraktykService.addPerson(newCoordinator.getOsoba(), addCoordinator);
if (addCoordinator !=null && addPerson !=null) {
return new ResponseEntity<KoordynatorzyPraktykEntity>(addCoordinator, HttpStatus.OK);
}
else {
return new ResponseEntity<KoordynatorzyPraktykEntity>(HttpStatus.NOT_FOUND);
}
}
Helper Class Koordynator:
public class Koordynator {
private KoordynatorzyPraktykEntity koordynator;
private OsobyEntity osoba;
public KoordynatorzyPraktykEntity getKoordynator() {
return koordynator;
}
public void setKoordynator(KoordynatorzyPraktykEntity koordynator) {
this.koordynator = koordynator;
}
public OsobyEntity getOsoba() {
return osoba;
}
public void setOsoba(OsobyEntity osoba) {
this.osoba = osoba;
}
}
and this is parsed JSON into controller through POSTMAN
{
"koordynator":
{
"doTestow" : "test"
},
"osoba":
{
"tytulZawodowy" : "inzynier",
"imie" : "Tomasz",
"nazwisko" : "Potempa",
"email" : "tp#tp.pl",
"telefonKomorkowy" : "124675484",
"telefonStacjonarny" : "654786484"
}
}
Only way I got it work
Class A:
#OneToMany(cascade = CascadeType.MERGE)
private List<B> b;
Class B:
#ManyToOne
#JoinColumn(name = "aId", referencedColumnName = "id")
private A a;
private String test;
Service:
A a = new A();
//Create without children
aFacade.create(a);
//items
List<B> list = new ArrayList<>();
B b = new B();
b.setTest("Hello");
b.setA(a);
list.add(b);
//merge
a.setB(list);
aFacade.edit(a);
you hit the exception below simply because the entity isn't in the Entity Manager's session at the moment you are trying to persist it. That's due to laziness of your association.
"detached entity passed to persist: praktyki.core.entities.KoordynatorzyPraktykEntity;"
Try calling em.merge() to attach it to the session.