I have two entities User:
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long userID;
#Column(name = "userHashedPassword")
private String password;
#Column(name = "userName")
private String userName;
#Column(name = "userEmail")
private String email;
#Transient
private List<String> groups = new LinkedList<>();
#ManyToMany
#JoinTable(name = "UserRoles",
joinColumns = #JoinColumn(
name = "userID"),
inverseJoinColumns = #JoinColumn(
name = "roleID"))
private Set<Role> roles = new HashSet<>();
#OneToMany(mappedBy = "user")
private Set<Rating> ratings;
protected User(){}
public User(String userHashedPassword, String userName, String email, Set<Role> roles){
this.password = userHashedPassword;
this.userName = userName;
this.email = email;
this.roles = roles;
}
//getters and setters
}
And Group:
#Table(name="FocusGroups")
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "groupID")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long groupID;
private String groupName;
#ManyToMany
#JoinTable(name = "GroupMembers",
joinColumns = #JoinColumn(
name = "groupID"),
inverseJoinColumns = #JoinColumn(
name = "userID"))
private Set<User> groupMembers = new HashSet<>();
#ManyToOne(fetch = FetchType.EAGER, optional = true)
#JoinColumn(name="frameworkID", nullable = true)
private Framework framework;
public Group(){}
public Group(String groupName, Set<User> groupMembers, Framework framework) {
this.groupName = groupName;
this.groupMembers = groupMembers;
this.framework = framework;
}
//getters setters
}
When I delete a User, I want to remove them from group members, however it fails due to foreign key constraint: java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (capripol.groupmembers, CONSTRAINT FK98tbu0sjfsn1m5p340dn0v8wo FOREIGN KEY (userID) REFERENCES users (userID))
How do I work around this?
Well, I will try to answer: First of all, it is rather strange you refer on Groups in user
entity like that:
#Transient
private List<String> groups = new LinkedList<>();
It this case, you will not have a column group in user table in database, hence you have to first perform removal from group_members for an all groups:
delete from group_members where userid = <user_id_you_want_to_remove>;
And only after your JoinTable table does not contain any refers to user with <user_id_you_want_to_remove>, than you can execute
delete from users where userid = 1;
Note: there is no matter you do it by spring data (e.g. deleteById(Long id) and using #Query annotation specify the query above in SQL or HQL, up to you) - this will work. But I highly recommend you to reconsider you database structure - it is not cute to store only one entity.
I have spring boot project using JPA/Hibernate, MySQL. I have three dao classes that have a many to many relationship.
The Poko classes look like this
Product
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false, columnDefinition = "integer")
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "price")
private Double price;
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(
name = "product_extra",
joinColumns = #JoinColumn(name="product_id"),
inverseJoinColumns = #JoinColumn(name="extra_id")
)
private List<Extra> extras = new ArrayList<>();
//constructor getters and setters
}
ProductExtra
#Entity
#Table(name = "product_extra")
public class ProductExtra {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false, columnDefinition = "integer")
private Integer id;
#Column(name = "product_id")
private Integer productId;
#Column(name = "extra_id")
private Integer extraId;
//constructor getters and setter
}
Extra
#Entity
#Table(name = "extra")
public class Extra {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false, columnDefinition = "integer")
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "price")
private Double price;
#ManyToMany(mappedBy = "extras")
private List<Product> products = new ArrayList<>();
//constructor getters and setters
}
The Extra repository with the query
public interface ExtraRepository extends JpaRepository<Extra, Integer> {
#Query("SELECT e.id, e.name, e.price FROM Extra e INNER JOIN ProductExtra pe ON e.id = pe.extraId WHERE pe.productId = ?1")
List<Extra> findExtraById(Integer productId);
}
The mapping in my controller
#GetMapping("/product/{productId}")
public List<Extra>getExtraById(#PathVariable("productId") final Integer productId){
return extraRepository.findExtraById(productId);
}
I am trying to make a many to many query to select The extras in each product, i am however getting this error Failed to convert from type [java.lang.Object[]] to type [#org.springframework.data.jpa.repository.Query the error message surprisingly also contains the results i want. Not sure what am doing wrong
Remove the SELECT clause:
#Query("FROM Extra join e.productExtra WHERE pe.productId = ?1")
Also keep in mind, that you not write an SQL Query, You work on Object, so for join you use the mapped property
I have a room entity, a rule entity and an amenities entity. What i want to do is have the rule and the amenities entity filled manually beforehand and when i insert a room, i want depending on what rules and amenities the user chose to map the two in two join tables, room_rules and room_amenities.
How am i supposed to achieve this? When i persist a new Room Entity, should it already contain the rules and the amenities? Is it going to work if the collection of those are not mapped as cascade.persist?
Or should i 1st persist the room without them and then map them to the room?
What should i do?
UPDATE
My RoomEntity:
public class RoomEntity {
#Id
#GeneratedValue(strategy= GenerationType.SEQUENCE)
#Column(name = "id", nullable =false, unique = true)
private Integer id;
#Basic
#Column(name = "title", nullable = false, length = 45)
private String title;
#Basic
#Column(name = "description", nullable = false, length = 300)
private String description;
#Basic
#Column(name = "square_meters", nullable = false)
private Integer square_meters;
#Basic
#Column(name = "overnight_price", nullable = false)
private double overnight_price;
#Basic
#Column(name = "exta_person_price", nullable = false)
private double exta_person_price;
#Basic
#Column(name = "max_people", nullable = false)
private Integer max_people;
#Basic
#Column(name = "min_overnights", nullable = false)
private Integer min_overnights;
#Basic
#Column(name = "beds", nullable = false)
private Integer beds;
#Basic
#Column(name = "bathrooms", nullable = false)
private Integer bathrooms;
#Basic
#Column(name = "bedrooms", nullable = false)
private Integer bedrooms;
#Basic
#Column(name = "transport", length = 300)
private String transport;
#Basic
#Column(name = "neightborhood", length = 300)
private String neightborhood;
#Basic
#Column(name = "house_rules", length = 200)
private String house_rules;
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = "room_host", referencedColumnName = "username", /*nullable = false,*/ insertable = false, updatable = false)
private HostEntity hostEntity;
public HostEntity getHostEntity() {
return hostEntity;
}
public void setHostEntity(HostEntity hostEntity) {
this.hostEntity = hostEntity;
}
#OneToOne
#JoinColumn(name = "room_type_id", referencedColumnName = "id", /*nullable = false,*/ insertable = false, updatable = false)
private TypeEntity typeEntity;
public TypeEntity getTypeEntity() {
return typeEntity;
}
public void setTypeEntity(TypeEntity typeEntity) {
this.typeEntity = typeEntity;
}
#OneToOne(cascade={CascadeType.ALL})
#JoinColumn(name = "room_location", referencedColumnName = "id"/*, nullable = false, insertable = false, updatable = false*/)
private LocationEntity locationEntity;
public LocationEntity getLocationEntity() {
return locationEntity;
}
public void setLocationEntity(LocationEntity locationEntity) {
this.locationEntity = locationEntity;
}
#OneToMany(cascade = {CascadeType.ALL})
#JoinTable(
/*name = "room_pictures",*/
joinColumns = {#JoinColumn(name = "room_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn (name ="picture_id", referencedColumnName = "id", unique = true)}
)
private Collection<PictureEntity> pictureEntities = new ArrayList<>();
public Collection<PictureEntity> getPictureEntities() {
return pictureEntities;
}
public void setPictureEntities(Collection<PictureEntity> pictureEntities) {
this.pictureEntities = pictureEntities;
}
#OneToMany
#JoinTable(
/*name = "room_amenities",*/
joinColumns = {#JoinColumn(name = "room_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn (name ="amenity_id", referencedColumnName = "id")}
)
private Collection<AmenitiesEntity> amenitiesEntities = new ArrayList<>();
public Collection<AmenitiesEntity> getAmenitiesEntities() {
return amenitiesEntities;
}
public void setAmenitiesEntities(Collection<AmenitiesEntity> amenitiesEntities) {
this.amenitiesEntities = amenitiesEntities;
}
#OneToMany
#JoinTable(
/*name = "room_amenities",*/
joinColumns = {#JoinColumn(name = "room_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn (name ="rule_id", referencedColumnName = "id")}
)
private Collection<RuleEntity> ruleEntities = new ArrayList<>();
public Collection<RuleEntity> getRuleEntities() {
return ruleEntities;
}
public void setRuleEntities(Collection<RuleEntity> ruleEntities) {
this.ruleEntities = ruleEntities;
}
public Collection<Picture> toPicturesModel(){
Collection<Picture> pictures = new ArrayList<>();
for(PictureEntity o : this.pictureEntities){
pictures.add(o.toModel());
}
return pictures;
}
public Collection<Amenities> toAmenitiesModel(){
Collection<Amenities> amenities = new ArrayList<>();
for(AmenitiesEntity o : this.amenitiesEntities){
amenities.add(o.toModel());
}
return amenities;
}
public Collection<Rule> toRulesModel(){
Collection<Rule> rules = new ArrayList<>();
for(RuleEntity o : this.ruleEntities){
rules.add(o.toModel());
}
return rules;
}
public Room toModel(Integer depth){
System.out.println("RoomEntity:::toModel()");
return new Room(
this.hostEntity == null? null : this.hostEntity.toModel(depth),
this.getTypeEntity() == null? null : this.typeEntity.toModel(),
this.getLocationEntity()== null? null : this.locationEntity.toModel(),
this.getPictureEntities() == null ? null : this.toPicturesModel(),
this.getAmenitiesEntities() == null? null : this.toAmenitiesModel(),
this.getRuleEntities() == null? null : this.toRulesModel(),
this.title, this.description, this.square_meters,
this.overnight_price, this.exta_person_price,
this.max_people, this.min_overnights, this.beds, this.bathrooms,
this.bedrooms, this.transport, this.neightborhood, this.house_rules);
}
}
My HostEntity inherits from RenterEntity the primary key username:
public class HostEntity extends RenterEntity {
#Basic
#Column(name = "about", length = 200)
private String about;
#Basic
#Column(name = "confirmed", nullable = false)
private boolean confirmed;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "hostEntity")
private Collection<RoomEntity> roomEntities;
public Collection<RoomEntity> getRoomEntities() {
return roomEntities;
}
public void setRoomEntities(Collection<RoomEntity> roomEntities) {
this.roomEntities = roomEntities;
}
My LocationEntity:
public class LocationEntity{
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
#Column(name = "id", nullable =false, unique = true)
private Integer id;
#Basic
#Column(name = "latitude", nullable = false, precision = 0)
private double latitude;
#Basic
#Column(name = "longitude", nullable = false, precision = 0)
private double longitude;
#Basic
#Column(name = "street_number", length = STREET_MAX)
private String street_number;
#Basic
#Column(name= "route", length = ROUTE_MAX)
private String route;
#Basic
#Column(name = "locality", length = LOCALITY_MAX)
private String locality;
#Basic
#Column(name = "postal_code", length = POSTAL_CODE_MAX)
private String postal_code;
#Basic
#Column(name = "country", length = COUNTRY_MAX)
private String country;
#Basic
#Column(name = "administrative_area_level_5", length = ADMINISTRATIVE_AREA_LEVEL_MAX)
private String administrative_area_level_5;
#Basic
#Column(name = "administrative_area_level_4", length = ADMINISTRATIVE_AREA_LEVEL_MAX)
private String administrative_area_level_4;
#Basic
#Column(name = "administrative_area_level_3", length = ADMINISTRATIVE_AREA_LEVEL_MAX)
private String administrative_area_level_3;
#Basic
#Column(name = "administrative_area_level_2", length = ADMINISTRATIVE_AREA_LEVEL_MAX)
private String administrative_area_level_2;
#Basic
#Column(name = "administrative_area_level_1", length = ADMINISTRATIVE_AREA_LEVEL_MAX)
private String administrative_area_level_1;
#Basic
#Column(name = "formatted_address", length = FORMATTED_ADRESS_MAX, nullable = false, unique = true)
private String formatted_address;
}
My TypeEntity:
public class TypeEntity {
#Id
#Column(name = "id", nullable = false, unique = true)
private Integer id;
#Basic
#Column(name = "type_name", nullable = false, length = 45)
private String type;
#OneToOne(mappedBy = "typeEntity")
private RoomEntity roomEntity;
}
My RuleEntity:
public class RuleEntity {
#Id
#Column(name = "id", nullable =false, unique = true)
//#GeneratedValue(strategy= GenerationType.SEQUENCE)
private Integer id;
#Basic
#Column(name = "rule", nullable =false , unique = true, length = 50)
private String rule;
}
My PictureEntity:
public class PictureEntity {
#Id
#Column(name = "id", nullable =false, unique = true)
#GeneratedValue(strategy= GenerationType.SEQUENCE)
private Integer picture_id;
#Basic
#Column(name = "thumbnail_url", nullable =false, length = 100)
private String thumbnail_url;
#Basic
#Column(name = "medium_url", nullable =false, length = 100)
private String medium_url;
#Basic
#Column(name = "picture_url", nullable =false, length = 100)
private String picture_url;
#Basic
#Column(name = "xl_picture_url", nullable =false, length = 100)
private String xl_picture_url;
}
My AmenitiesEntity:
public class AmenitiesEntity {
#Id
#Column(name = "id", nullable =false, unique = true)
//#GeneratedValue(strategy= GenerationType.SEQUENCE)
private Integer id;
#Basic
#Column(name = "name", nullable = false, unique = true, length = 50)
private String amenity;
}
My code that creates a room is this:
public Room addRoom(RoomInput room) {
dao.setParam("username", room.getOwner());
List<HostEntity> owner = dao.getTuples("users.findByUsername");
RoomEntity newRoom = new RoomEntity(room.getTitle(), room.getDescription(), room.getSquare_meters(), room.getOvernight_price(),
room.getExta_person_price(), room.getMax_people(), room.getMin_overnights(), room.getBeds(), room.getBathrooms(),
room.getBedrooms(), room.getTransport(), room.getNeightborhood(), room.getHouse_rules());
newRoom.setLocationEntity(room.getLocation().toEntity());
for(Picture o :room.getPictures()){
newRoom.getPictureEntities().add(o.toEntity());
}
RoomEntity entity = dao.insertTuple(newRoom);
entity.setHostEntity(owner.get(0));
for(Amenities o : room.getAmenities()){
entity.getAmenitiesEntities().add(o.toEntity());
}
for(Rule o : room.getRules()){
entity.getRuleEntities().add(o.toEntity());
}
entity.setTypeEntity(room.getType().toEntity());
entity = dao.updateTuple(entity);
System.out.println(entity.toString());
return (entity == null) ? null : entity.toModel(0);
}
What happens now is that, even though my 3 join tables,
room_pictures, room_rules, room_amenities get updated correctly,
the room i put in the database has the foreign key of the host(username)
and the type(id) null.
Is there a better way in doing all this? Right now i 1st persist the new room,
and cascade persist the pictures and the location and then i merge the room with the new values of host, amenities and rules updated.
UPDATE
I fixed the problem. Now i do one insert for the entities i want to persist with the room, aka location and pictures and one merge to connect the detatched entities amenities/rules in the join tables and host/type foreign keys in the room table. The problem with host and type was that i had in the #JoinColumn updateable and instertable to be false which was wrong. After i deleted those statements, it worked! My code in the end to persist/merge the detatched and new entities is this:
public Room addRoom(RoomInput room) {
dao.setParam("username", room.getOwner());
HostEntity host = null;
List<HostEntity> owner = dao.getTuples("users.findByUsername");
RoomEntity newRoom = new RoomEntity(room.getTitle(), room.getDescription(), room.getSquare_meters(), room.getOvernight_price(),
room.getExta_person_price(), room.getMax_people(), room.getMin_overnights(), room.getBeds(), room.getBathrooms(),
room.getBedrooms(), room.getTransport(), room.getNeightborhood(), room.getHouse_rules());
System.out.println(owner.get(0).toString());
newRoom.setLocationEntity(room.getLocation().toEntity());
for(Picture o :room.getPictures()){
newRoom.getPictureEntities().add(o.toEntity());
}
RoomEntity entity = dao.insertTuple(newRoom);
for(Amenities o : room.getAmenities()){
entity.getAmenitiesEntities().add(o.toEntity());
}
for(Rule o : room.getRules()){
entity.getRuleEntities().add(o.toEntity());
}
entity.setHostEntity(owner.get(0));
entity.setTypeEntity(room.getType().toEntity());
dao.updateTuple(entity);
System.out.println(entity.toString());
return (entity == null) ? null : entity.toModel(0);
}
I have two tables
#Entity
#Table(name = "publicSitePortfolioWorks")
public class PublicSitePortfolioWorks implements java.io.Serializable {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "mainImage")
private FrameworkFiles frameworkFilesByMainImage;
#Column(name = "androidImage")
private FrameworkFiles frameworkFilesByAndroidImage;
#Column(name = "bigImage")
private FrameworkFiles frameworkFilesByBigImage;
#Column(name = "mainName")
private String mainName;
#Column(name = "androidName")
private String androidName;
#Column(name = "url")
private String url;
#Column(name = "shortText")
private String shortText;
#Column(name = "`fullText`")
private String fullText;
#Column(name = "linkedObjectNameLocal")
private String linkedObjectNameLocal;
#Column(name = "`sort`")
private Integer sort;
#Column(name = "active")
private Integer active;
#OneToMany
#JoinTable(
name = "framework_files",
joinColumns = {
#JoinColumn(
// table = "framework_files",
name = "linkedObjectName",
referencedColumnName = "linkedObjectNameLocal"),
#JoinColumn(
// table = "framework_files",
name = "linkedObjectId",
referencedColumnName = "id"
)
}
// , inverseJoinColumns = #JoinColumn(name = "linkedObjectName")
)
private Set<FrameworkFiles> filesLocal = new HashSet<FrameworkFiles>(0);
And second table
#Entity
#Table(name = "framework_files")
public class FrameworkFiles implements java.io.Serializable {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "fileName")
private String fileName;
#Column(name = "fileSize")
private Float fileSize;
#Column(name = "filePath")
private String filePath;
#Column(name = "fileUrl")
private String fileUrl;
#Column(name = "linkedObjectId")
private Integer linkedObjectId;
#Column(name = "linkedObjectName")
private String linkedObjectName;
#Column(name = "active")
private Integer active;
#Column(name = "uploadDate")
private Date uploadDate;
#Column(name = "uploadersIP")
private String uploadersIp;
#Column(name = "uploadersIPXFORWARDEDFOR")
private String uploadersIpxforwardedfor;
#Column(name = "userName")
private String userName;
When I have started my application, I got exception:
Caused by: org.hibernate.MappingException: Foreign key (FK_hksv6kkf3cysf5ug30q575tg1:framework_files [filesLocal_id])) must have same number of columns as the referenced primary key (framework_files [linkedObjectName,linkedObjectId,filesLocal_id])
at org.hibernate.mapping.ForeignKey.alignColumns(ForeignKey.java:110)
Please advice, how to fix this exception.
I have seen many topics on stackoverflow about this Exception, but I can't understand, how to fix this error in my code.
I'm trying to implement a Keyword search functionality that returns a List of Keyword entities based on a field text match.
Right now, the query
select * from photo_keywords pk
inner join keywords k on pk.photo_id = k.keyword_id
inner join photos p on pk.keyword_id = p.photo_id
where k.keyword LIKE "%$SOME_SEARCH_VALUE%";
returns all matching photos for a given keyword search. I'd like to have this adapted to a #NamedQuery with the following Entity objects:
#Entity
#Table(name = "keywords")
public class Keyword implements Serializable{
#Id
#Column(name = "keyword_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column
private String keyword;
#ManyToMany(mappedBy = "keywords")
private List<Photo> photos;
//getters and setters
}
and
#Entity
#Table(name = "photos")
public class Photo implements Serializable{
#Id
#Column(name = "photo_id", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "photo_name", nullable = false)
private String photoName;
#Column(name = "photo_path", nullable = false)
private String photoPath;
#Column(name = "upload_date", nullable = false)
private Date uploadDate;
#Column(name = "view_count", nullable = false)
private int viewCount;
#Column(name = "capture_date", nullable = false)
private Date captureDate;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "photo_metadata")
#MapKeyColumn(name = "metadata_name")
#Column(name = "metadata_value")
private Map<String, String> photoMetadata;
#ManyToMany
#JoinTable(name = "photo_keywords",
joinColumns = #JoinColumn(name = "keyword_id"),
inverseJoinColumns = #JoinColumn(name = "photo_id"))
public List<Keyword> keywords;
//getters and setters
}
This creates a join table photo_keywords, rather than a JoinColumn.
What I've tried so far with the Keyword entity:
#NamedQueries({
#NamedQuery(
name = "findKeywordByName",
query = "SELECT keyword from Keyword k WHERE k.keyword = :keyword"
)
})
which is executed via
public Keyword findKeywordByString(String keyword){
Keyword thisKeyword;
Query queryKeywordExistsByName = getEntityManager().createNamedQuery("findKeywordByName");
queryKeywordExistsByName.setParameter("keyword", keyword);
try {
thisKeyword = new Keyword((String) queryKeywordExistsByName.getSingleResult());
} catch (NoResultException e){
thisKeyword = null;
}
return thisKeyword;
}
This returns the Keyword, but with the photos property being null. This is to be expected, since I'm only selecting the keyword property. How can I adapt the SQL query above to a #NamedQuery?