How can you rename a field name in a table which generates Hibernate?
When you create:
#ManyToMany(targetEntity = GroupRightEntity.class)
or
#ManyToMany(targetEntity = UserRightEntity.class)
Now made with an explicit change of names of columns in the Database.
alter table security_mapping_user rename column sec_mapping_id to secmappingentity_id;
Thank you for your help and understanding.
Hibernate generates links for additional tables. Accordingly, I understand that the generated field names. I need to use annotations, or something else, these column names change. I hope I wrote everything correctly.
We have.
#Entity
#Table(name = "ROLE")
public class RoleEntity implements Role, Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "SEQ_ROLE")
#SequenceGenerator(name = "SEQ_ROLE", sequenceName="SEQ_ROLE", allocationSize = 1)
private Long id;
private String name;
private String description;
#Entity
#Table(name = "URL")
public class UrlEntity implements Url, Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "SEQ_URL")
#SequenceGenerator(name="SEQ_URL", sequenceName="SEQ_URL", allocationSize = 1)
private Long id;
private String url;
#OneToMany(targetEntity = RoleEntity.class, fetch = FetchType.EAGER, mappedBy="url_id")
private Set<Role> roles;
Later we get a third table. "URL_ROLE"
In this table, the fields are named.
"URL_ID" and "ROLE_ID"
"URL_ID" need to rename the field in the "urlentity_id".
It seems now I built everything correctly.
When defining a ManyToMany you should also define which column your relationship maps on. That's where you should change the name.
Related
I'm trying to make a relation between my Book entity and a list of languages that I retrieve through a service.
In my database, each book has a: ID, TITLE, CATEGORY_ID (FK), LANG_ID
Book.java:
#Entity
#Table(schema = Constants.SHEMA, name = "Book")
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private long id;
#Column(name = "TITLE")
private String title;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "CATEGORY_ID")
private Category category;
private Language language; // -> The Column associated in the database is Long LANG_ID
}
Category.java:
#Entity
#Table(schema = Constants.SHEMA, name = "Category")
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
}
Language.java:
public class Language implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
}
I understood the relation between Book & Category as both of them are tables in my database. However, Language is something that I get from a service and isn't persisted in my database.
The languages I get are just an ID and a Name for the language.
My question is: In order to link the language ID to my LANG_ID (the ID of the language in my Book table), what annotation (ManyToOne, Entity, ...) should I write for Language? Should I also put it in my persistence.xml ? I tried a couple but it seems like it's not working well.
Thank you very much
I don't think it is good practice to mix persisted data with non-persisted data as it can cause other unexpected problems. Anyway you can try something like this:
#Entity
#Table(schema = Constants.SHEMA, name = "Book")
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private long id;
#Column(name = "TITLE")
private String title;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "CATEGORY_ID")
private Category category;
#Column(name = "LANG_ID")
private Integer langId;
#Transient
private Language language;
#PostLoad
public void loadLanguage() {
// get the language data here
}
}
The language field has no database table, so you cannot use any mapping annotation. From the Java EE docs:
public #interface Transient
Specifies that the property or field is not persistent. It is used to annotate a property or field of an entity class, mapped superclass, or embeddable class.
Example:
#Entity
public class Employee {
#Id int id;
#Transient User currentUser;
...
}
The #PostLoad annotation declares a method to be called after the entity is loaded:
public #interface PostLoad
Specifies a callback method for the corresponding lifecycle event. This annotation may be applied to methods of an entity class, a mapped superclass, or a callback listener class.
First of all, did you consider to store language in your database? I mean language are mostly the same, doesn't change too often, you can also store in a properties file and read them at runtime to use them later.
Anyway, I think you should:
first get from external system languages
store in variable / in memory cache ( like a Map<Long,String> where you can store id and name )
read your data from database
for each row you do
read book language id, read the cache, get out data you need
If you can't change model, just use a dto with your entity and the language and you're fine
I have a table PATIENT which has some fields. There's also a CONTACT table that has a field called 'patientId' that needs to store PATIENT's ID (which is autogenerated), and a PATIENT_CONTACT table that only relates the two tables.
Now, here comes the tricky part. There are three other tables: CONTACT_ADDRESS, CONTACT_PHONE, CONTACT_EMAIL. A row in CONTACT will have the same ID as one (and only one) of CONTACT_ADDRESS, CONTACT_PHONE and CONTACT EMAIL. How do I get this all to work?
I have tried so many approaches, this is what I have right now:
#Entity
#Table(name = "patient", schema = "patient")
public class PatientEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
//... more fields
#OneToOne
private ContactEmailEntity contactEmailEntity;
#OneToOne
private ContactAddressEntity contactAddressEntity;
#OneToOne
private ContactPhoneEntity contactPhoneEntity;
}
The three CONTACT_* classes are similar and they look like this:
#Table(name = "contact_address", schema = "patient")
public class ContactAddressEntity {
#Id
#Column(name = "id")
private Long id;
// ... more fields
#OneToOne(cascade = {CascadeType.ALL})
#MapsId
private ContactEntity contact;
}
And my CONTACT class looks like this:
#Table(name = "contacto", schema = "paciente")
public class ContactEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
//... more fields
Can you see things that don't look right or could be done better? I get all sorts of errors with every approach. My latest one is:
ERROR: column patientent0_.contact_address_entity_contact_id does not exist
when trying to do a simple patient find. Please, any help is appreciated!
I am using Spring boot. To save my entities on relational database, I configured a datasource and my domain classes, e.g.:
#Entity
#Table(schema = "schema_name", name = "tb_name")
public class table_name extends DomainEntity<Long> {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID_TABLE_NAME", nullable = false, updatable = false, precision = 12)
#SequenceGenerator(name = "sqTableName", sequenceName = "SQ_TABLE_NAME", initialValue = 1, allocationSize = 1)
#GeneratedValue(generator = "sqTableName", strategy = GenerationType.SEQUENCE)
private Long id;
#NotNull
#ManyToOne
#JoinColumn(name = "ID_OTHER_COLUMN", referencedColumnName = "ID_OTHER_COLUMN", nullable = false)
private OtherObject obj;
Using this tutorial: https://www.baeldung.com/spring-data-redis-tutorial, I configured my domain class Student:
#RedisHash("Student")
public class Student implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
public enum Gender {
MALE, FEMALE
}
private String id;
private String name;
private Gender gender;
private int grade;
}
When a class is anotated with #RedisHash, when I use .save method, it saves this entity on Redis.
I would like to use this domain class in a relational database WITHOUT duplicating the file to save, sometimes on Redis and sometimes in a relational database. I searched, but I didn't find anything.
Can someone help?
You could define the domain class without the annotation #RedisHash and make an empty subclass of the domain class for each database.
The domain class for redis would have the #RedisHash annotation
I've got the following classes/relationship (getters & setters not displayed, but present):
public class Contract implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
private String number;
private String volume;
#OneToMany(cascade=CascadeType.REMOVE)
#JoinTable(joinColumns = #JoinColumn(name = "contract_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "attachment_id", referencedColumnName = "id"))
private List<Attachment> attachments;
}
public class Attachment implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
#Lob
#Basic(fetch=FetchType.LAZY)
#Column(length=2147483647)
private byte[] contents;
private String name;
}
As per my needs/design, I am using a join table.
If I want to delete an attachment from the Contract, I need to load the Contract, and then loop through all the attachments until I find the one I want to remove and remove it from the list.
Although this is functional, it will require a lot of DB communication. If the list of attachments is long, and contains large contents, it will also require large bandwidth.
Is there any other way I can remove it? If I try to remove the attachment directly (ex: Attachment.findById().delete()), it will fail due to the FK relationship - won't it? (I haven't tried this yet, but I suspect it).
Additionally, if I have a very large list of attachments, iterating through them one by one until I find the correct one is not very efficient either.
Does JPA provide any other/better solution?
There's one workaround solution I know - you can create an entity class for join table.
You will have to give a name to your join table within #JoinTable annotation (name attribute), lets say ContractAttachment. Then you can create entity:
#Entity(name = "ContractAttachment") // note the same name of table
#IdClass(ContractAttachmentId.class)
public class ContractAttachment implements Serializable {
static final long serialVersionUID = 1L;
#Id
#ManyToOne
#JoinColumn(name="contract_id") // same mappings for columns
private Contract contract;
#Id
#ManyToOne(cascade = CascadeType.REMOVE)
#JoinColumn(name="attachment_id") // same mappings for columns
private Attachment attachment;
// you will also have to override equals and hashcode methods here
}
The class ContractAttachmentId should look like:
public class ContractAttachmentId implements Serializable {
private long contract; // note the same fields names
private long attachment;
// this class should also implement hashcode and equals
}
Now you can remove a single entry in join table and even cause attachment object to be deleted too.
ContractAttachment ca = em.createQuery("select ca from ContractAttachment ca " +
"where ca.contract = :contract and ca.attachment = :attachment")
.setParameter("contract", selectedContract)
.setParameter("attachement", selectedAttachment)
.getSingleResult();
em.remove(ca);
I have this error
The collection-valued path 'c.medecin' cannot be resolved to a valid association field
The state field path 'm.id' cannot be resolved to a valid type.
when I execute this request
createQuery("select c from Creneaux c join c.medecin m where m.id=:idMedecin").setParameter("idMedecin", medecin.getId());
I use these 2 tables : MEDECINS(ID) and CRENEAUX(ID, ID_MEDECIN)
#Entity
#Table(name = "medecins")
#XmlRootElement
public class Medecins implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "medecin"/*"idMedecin"*/)
private transient List<Creneaux> creneauxList;
}
and
#Entity
#Table(name = "creneaux")
public class Creneaux implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Long id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="ID_MEDECIN")
private transient Medecins medecin;
#Column(name = "ID_MEDECIN")
private BigInteger idMedecin;
I begin in JPA, so I a not sure about all the code. I think the Query is correct, but I don't know how to annotate the entities to make the Query valid.
Thanks
I wonder why adding the last attribute in the second Entity that is: idMedecin while you've already joined the two entities to each others. Maybe you should omit it.
If the purpose of the query is to select all the creneaux related to the given medecin, then you ought to change the query to:
createQuery("SELECT c FROM Creneaux c WHERE c.medecin.id = :idMedecin").setParameter("idMedecin", medecin.getId());
Where medecin.getId() should be provided before the query's execution.
That's because the Creneaux.medecin field is transient. Transient fields are ignored by JPA.
Another thing is that you don't have two join these two entities. If you want to filter by Medecin ID it's enough to execute a query as #Omar described.