so I have an hibernate Entity called Appointment, in this entity I have a AppointNumber property which itself contains a number property which is a string.
When I persist my Appointment, I need the AppointmentNumber. I got it to work with #Embedded and #Embeddable the other day but this creates a join table Which I can't have.
I tried many other solutions to try and get it to work without join tables but I can't figure it out. (I get lots of ava.lang.IllegalStateException)
Can anyone help?
Thanks!
#Entity(name = "appointments")
public class Appointment {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#OneToOne(mappedBy = "number")
#Fetch(value = FetchMode.SELECT)
private AppointmentNumber appointmentNumber;
Appointment entity
AppointmentNumber, used in Appointment but should not be an entity
public class AppointmentNumber {
#OneToOne
#JoinColumn(name = "appointmentNumber", unique = true, nullable = false)
private String number;
You could do like this:
#Entity(name = "appointments")
public class Appointment {
///....
#Convert(converter = AppointmentNumberConverter.class)
private AppointmentNumber appointmentNumber;
///....
}
#Converter
public class AppointmentNumberConverter implements
AttributeConverter<PersonName, String> {
#Override
public String convertToDatabaseColumn(AppointmentNumber appointmentNumber) {
if (appointmentNumber == null) {
return null;
}
return appointmentNumber.getNumber();
}
#Override
public AppointmentNumber convertToEntityAttribute(String appointmentNumber) {
if (appointmentNumber == null) {
return null;
}
AppointmentNumber result = new AppointmentNumber();
result.setNumber(appointmentNumber);
return result;
}
}
Have a look at JPA Converter documentation.
I have spring boot application which use spring data and hibernate to fetch and insert data to database.
I have one-to-many table relation:
#Entity
#Data
#EqualsAndHashCode(of = { "id" })
#Table(name = "direction")
public class Direction {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "name")
private String name;
}
and
#Entity
#Table(name = "subdivision")
#Data
#EqualsAndHashCode()
public class Subdivision {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "name")
private String name;
#ManyToOne()
#JoinColumn(name = "direction_id", referencedColumnName = "id")
private Direction direction;
}
I have lombok plugin for boilarplate code generation.
I also have repository
public interface SubdivisionRepository extends CrudRepository<Subdivision, Long> {
List<Subdivision> findAll();
List<Subdivision> findByDirection(Direction direction);
}
and service
#Service
public class SubdivisionServiceImpl implements SubdivisionService {
#Autowired
private SubdivisionRepository subdivisionRepository;
#Override
public List<Subdivision> findAll() {
return subdivisionRepository.findAll();
}
#Override
public Subdivision findById(Long id) {
return subdivisionRepository.findById(id).get();
}
#Override
#Transactional
public void save(Subdivision subdivision) {
subdivisionRepository.save(subdivision);
}
#Override
public List<Subdivision> findByDirection(Direction direction) {
return subdivisionRepository.findByDirection(direction);
}
}
That's all. Then I try to update subdirection by changing direction type it shows hibernate exception: Error during managed flush [org.hibernate.HibernateException: identifier of an instance of com.entity.Direction was altered from 2 to 3]
I found the same question on stackoverflow but nothing suggested helped.
I tried to change fetch type and cascade type but it didn't helped.
Does anyone have solution?
P.S Here the code how I update entity
public void updateSubdivision(Subdivision subdivision){
Direction d = directionService.findById(subdivision.getDirection().getId());
Subdivision s = new Subdivision();
s.setDirection(d);
s.setName(subdivision.getName());
s.setId(subdivision.getId());
subdivisionService.save(s);
}
It's controller method
I am using JPA 2.0 in my project and I have used #ElementCollection on one of my property in the Pojo class.
Following is my java entity class :
#Entity
#Embeddable
#Table(name = "test")
public class Test {
#Id
#GeneratedValue(generator = "seq_test")
#SequenceGenerator(name = "seq_test", sequenceName = "seq_test")
#Column(name = "id")
private Long id;
#ElementCollection(fetch = FetchType.LAZY, targetClass = String.class)
#CollectionTable(name = "denied_set")
#OrderColumn
private Set<String> deniedSet;
#ElementCollection(fetch = FetchType.LAZY, targetClass = String.class)
#CollectionTable(name = "masked_set")
#OrderColumn
private Set<String> maskedSet;
#ElementCollection(fetch = FetchType.LAZY, targetClass = String.class)
#CollectionTable(name = "user_groups")
#OrderColumn
private Set<String> userGroups;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Set<String> getDeniedSet() {
return deniedSet;
}
public void setDeniedSet(Set<String> deniedSet) {
this.deniedSet = deniedSet;
}
public Set<String> getMaskedSet() {
return maskedSet;
}
public void setMaskedSet(Set<String> maskedSet) {
this.maskedSet = maskedSet;
}
public Set<String> getUserGroups() {
return userGroups;
}
public void setUserGroups(Set<String> userGroups) {
this.userGroups = userGroups;
}
}
I am using Spring Data JPA Repositories for interaction with database. Data is properly inserted into the database table. When I delete some data from the table, I can see in the logs that the delete queries are executed on the child tables also and after that the same data is reinserted into the table.
Can anyone help me understand if I am missing anything in the code ?
Thanks in advance.
Just some questions that might help you:
Do you need #Embedded public class Test? If you are not using it in
another owning entity then remove it.
Specify which column should be used as id for the
#ElementCollection. Use something like this:
#CollectionTable(name="denied_set",
JoinColumns=#JoinColumn(name="COLUMN_NAME")) or use Embedded if the
deniad_set is Embeddable.
Fetch type is lazy by default on ElementCollection and do you really need targetClass there?
Otherwise it looks correct to me...
I know how to implement spring data repositories,
Create an interface like this :
public interface CountryRepository extends CrudRepository<Country, Long> {}
Now Country is an AbstractCatalog and I have (a lot) more catalogs in my project.
I'm wondering if I can make only one repository that would work for all the catalogs:
public interface AbstractCatalogRepository extends CrudRepository<AbstractCatalog, Long> {}
Now I don't see a problem while saving, but if I want to search an AbstractCatalog I'm already sure that I'll hit the wall because the repository will not know which sub-class it must choose.
AbstractCatalog.class
#MappedSuperclass
public abstract class AbstractCatalog extends PersistentEntity {
/**
* The Constant serialVersionUID.
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
/**
* The code.
*/
#Column(unique = true, nullable = false, updatable = false)
private String code;
/**
* The description.
*/
#Column(nullable = false)
private String description;
/**
* The in use.
*/
#Column(name = "IN_USE", nullable = false, columnDefinition = "bit default 1")
private Boolean inUse = Boolean.TRUE;
// getters and setters
}
Country.class
#Entity
#Table(name = "tc_country")
#AttributeOverrides({
#AttributeOverride(name = "id", column =
#Column(name = "COUNTRY_SID")),
#AttributeOverride(name = "code", column =
#Column(name = "COUNTRY_CODE")),
#AttributeOverride(name = "description", column =
#Column(name = "COUNTRY_DESCRIPTION"))})
public class Country extends AbstractCatalog {
public static final int MAX_CODE_LENGTH = 11;
#Column(name = "GEONAMEID", nullable = true, unique = false)
private Long geonameid;
// getter and setter
}
Has anyone any idea, how I could use only ONE repository for all the implementations of AbstractCatalog class without having to create the same interface over and over again with minimal differences in name and other properties?
If you aren't using table inheritance on the database side (e.g. super class table with descriminator column), AFAIK, and based off reading the JPA tutorial, this can't be done (i.e. simply using #MappedSuperclass annotation for your abstract class)
Mapped superclasses cannot be queried and cannot be used in EntityManager or Query operations. You must use entity subclasses of the mapped superclass in EntityManager or Query operations. Mapped superclasses can't be targets of entity relationships
Note, the JPA repository abstraction uses an EntityManager under the hood. I did a simple test, and what you will get (in the case of Hibernate implementation) an "IllegalArgumentException : not an entity AbstractClass"
On the other hand, if you do use table inheritance, then you can use the abstract type. I know you said "with just the minimal change" (and I guess my short answer is I don't think it's possible - probably for the reasons you guessed), so I guess the rest of this answer is for other inquiring minds ;-)
An example of a table inheritance strategy would be something like this (disclaimer: this is not the correct visualization for erd inheritance, but MySQL Workbench doesn't support it, but what I have below forward engineered the model to MYSQL the way it needs to be)
Where CountryCatalog has a FK/PK reference to the AbstractCatalog table pk (id). The AbstractCatalog table has a descriminatorColumn that will be used to determine to which subtype the supertype occurrence is related.
In terms of how you would code that, it would look something like
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name="descriminatorColumn")
#Table(name="AbstractCatalog")
public abstract class AbstractCatalog {
#Id
private long id;
...
}
#Entity
#Table(name = "CountryCatalog")
public class CountryCatalog extends AbstractCatalog {
// id is inherited
...
}
public interface AbstractCatalogRepository
extends JpaRepository<AbstractCatalog, Long> {
}
#Repository
public class CountryCatalogServiceImpl implements CountryCatalogService {
#Autowired
private AbstractCatalogRepository catalogRepository;
#Override
public List<CountryCatalog> findAll() {
return (List<CountryCatalog>)(List<?>)catalogRepository.findAll();
}
#Override
public CountryCatalog findOne(long id) {
return (CountryCatalog)catalogRepository.findOne(id);
}
}
Basically, in conclusion, what you are trying to do won't work if you don't have table inheritance. The class type for the repository needs to be an entity. If your tables aren't set up this way for inheritance, it just comes down to whether or not you want to change the tables. It may be a bit much just to avoid multiple repositories though.
Some references I used are here and here
Note: Everything in this answer is tested against Hibernate provider
Oke, new project and I'm following this set up a little bit.
The problem was :
We want to add attachments, but an attachment can be uploading a file, a link or a mail.
Pojo classes :
Attachment.java :
#Entity
#Table(name = "T_ATTACHMENT")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING)
public abstract class Attachment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ATTACHMENT_SID")
private Long id;
#ManyToOne
#JoinColumn(name = "TASK_SID", referencedColumnName = "TASK_SID", nullable = false, unique = false, insertable = true, updatable = true)
private Task task;
#ManyToOne
#JoinColumn(name = "USER_SID", referencedColumnName = "USER_SID", nullable = false, unique = false, insertable = true, updatable = true)
private User user;
public Task getTask() {
return task;
}
public void setTask(Task task) {
this.task = task;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
FileAttachment.java :
#Entity
#Table(name = "T_FILE_ATTACHMENT")
#DiscriminatorValue("FILE")
public class FileAttachment extends Attachment {
#Column(name = "NAME", nullable = false, unique = false)
private String fileName;
#Lob
#Basic
#Column(name = "FILE", nullable = false, unique = false)
private byte[] file;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public byte[] getFile() {
return file;
}
public void setFile(byte[] file) {
this.file = file;
}
}
MailAttachment.java :
#Entity
#Table(name = "T_MAIL_ATTACHMENT")
#DiscriminatorValue("MAIL")
public class MailAttachment extends Attachment {
#Column(name = "RECIPIENT", nullable = false, unique = false)
private String to;
#Column(name = "CC", nullable = true, unique = false)
private String cc;
#Column(name = "BCC", nullable = true, unique = false)
private String bcc;
#Column(name = "TITLE", nullable = true, unique = false)
private String title;
#Column(name = "MESSAGE", nullable = true, unique = false)
private String message;
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getCc() {
return cc;
}
public void setCc(String cc) {
this.cc = cc;
}
public String getBcc() {
return bcc;
}
public void setBcc(String bcc) {
this.bcc = bcc;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
LinkAttachment.java :
#Entity
#Table(name = "T_LINK_ATTACHMENT")
#DiscriminatorValue("LINK")
public class LinkAttachment extends Attachment {
#Column(name = "DESCRIPTION", nullable = true, unique = false)
private String description;
#Column(name = "LINK", nullable = false, unique = false)
private String link;
public String getDescription() {
return description == null ? getLink() : description;
}
public void setDescription(String description) {
this.description = description;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
}
Spring data repo's :
AttachmentRepository.java:
public interface AttachmentRepository extends CustomRepository<Attachment, Long> {
List<Attachment> findByTask(Task task);
}
CustomRepository.java :
public interface CustomRepository<E, PK extends Serializable> extends
PagingAndSortingRepository<E, PK>,
JpaSpecificationExecutor<E>,
QueryDslPredicateExecutor<E> {
#Override
List<E> findAll();
}
And at last the service :
#Service
public class AttachmentServiceImpl implements AttachmentService {
#Inject
private AttachmentRepository attachmentRepository;
#Override
public List<Attachment> findByTask(Task task) {
return attachmentRepository.findByTask(task);
}
#Override
#Transactional
public Attachment save(Attachment attachment) {
return attachmentRepository.save(attachment);
}
}
This results in :
I can save to the abstract repo with any implementation I created, JPA will do it correct.
If I call findByTask(Task task) I get a List<Attachment> of all the subclasses, and they have the correct subclass in the back.
This means, you can make a renderer who do instanceof and you can customize your rendering for each subclass.
Downside is, you still need to create custom specific repository's, but only when you want to query on a specific property what is in the subclass or when you only want 1 specific implementation in stead of all implementations.
What DB are you using?
If it's JPA, take a look at
Can I use a generic Repository for all children of a MappedSuperClass with Spring Data JPA?
If it's Mongo you need to properly tune Jackson polymorphism configuration
http://wiki.fasterxml.com/JacksonPolymorphicDeserialization
So this is possible.
I'm using Spring with Hibernate as a JPA provider and are trying to get a #OneToMany (a contact having many phonenumbers) to save the foreign key in the phone numbers table. From my form i get a Contact object that have a list of Phone(numbers) in it. The Contact get persisted properly (Hibernate fetches an PK from the specified sequence). The list of Phone(numbers) also gets persisted with a correct PK, but there's no FK to the Contacts table.
public class Contact implements Serializable {
#OneToMany(mappedBy = "contactId", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
private List<Phone> phoneList;
}
public class Phone implements Serializable {
#JoinColumn(name = "contact_id", referencedColumnName = "contact_id")
#ManyToOne
private Contact contactId;
}
#Repository("contactDao")
#Transactional(readOnly = true)
public class ContactDaoImpl implements ContactDao {
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void save(Contact c) {
em.persist(c);
em.flush();
}
}
#Controller
public class ContactController {
#RequestMapping(value = "/contact/new", method = RequestMethod.POST)
public ModelAndView newContact(Contact c) {
ModelAndView mv = new ModelAndView("contactForm");
contactDao.save(c);
mv.addObject("contact", c);
return mv;
}
}
Hopefully I got all of the relevant bits above, otherwise please let me know.
You have to manage the Java relationships yourself. For this kind of thing you need something like:
#Entity
public class Contact {
#Id
private Long id;
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "contact")
private List<Phone> phoneNumbers;
public void addPhone(PhoneNumber phone) {
if (phone != null) {
if (phoneNumbers == null) {
phoneNumbers = new ArrayList<Phone>();
}
phoneNumbers.add(phone);
phone.setContact(this);
}
}
...
}
#Entity
public class Phone {
#Id
private Long id;
#ManyToOne
private Contact contact;
...
}
In reply to Cletus' answer. I would say that it's important to have the #column annotation on the id fields, as well as all the sequence stuff. An alternative to using the mappedBy parameter of the #OneToMany annotation is to use the #JoinColumn annotation.
As a kinda aside your implementation of addPhone needs looking at. It should probably be something like.
public void addPhone(PhoneNumber phone) {
if (phone == null) {
return;
} else {
if (phoneNumbers == null) {
phoneNumbers = new ArrayList<Phone>();
}
phoneNumbers.add(phone);
phone.setContact(this);
}
}
If the Contact-Phone relationship is unidirectional, you can also replace mappedBy in #OneToMany annotation with #JoinColumn(name = "contact_id").
#Entity
public class Contact {
#Id
private Long id;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "contact_id")
private List<Phone> phoneNumbers;
// normal getter/setter
...
}
#Entity
public class PhoneNumber {
#Id
private Long id;
...
}
Similar in JPA #OneToMany -> Parent - Child Reference (Foreign Key)
I don't think the addPhone method is necessary, you only have to set the contact in the phone object:
phone.setContact(contact);
If you want your relationship unidirectional i.e. can navigate from Contact to Phone's only, you need to add
#JoinColumn(name = "contact_id", nullable = false)
Under your #OneToMany on your parent entity.
nullable = false IS VITAL if you want hibernate to populate the fk on the child table
Try this sample:
#Entity
public class Contact {
#Id
private Long id;
#JoinColumn(name = "contactId")
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Phone> phones;
}
#Entity
public class Phone {
#Id
private Long id;
private Long contactId;
}
In JPA this helped me
contact.getPhoneList().forEach(pl -> pl.setContact(contact));
contactRepository.save(contact);