Here is my table design
**Songs
Movie Id - integer
[year] - integer
Song name - character
Set Singers(list) - text
Lyrics writer(list) -text
Length - numeric(3,2)**
I am developing a rest service using spring boot . I tried to implement a pojo where I am not getting good result.
here is my developed model class
#Entity
public class SongsInfo {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
int year;
#ElementCollection
List<String> singers;
#ElementCollection
List<String> lyricists;
float length;
public SongsInfo() {
}
public int getYear() {
return year;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setYear(int year) {
this.year = year;
}
public List<String> getSingers() {
return singers;
}
public void setSingers(List<String> singers) {
this.singers = singers;
}
public List<String> getLyricists() {
return Lyricists;
}
public void setLyricists(List<String> lyricists) {
Lyricists = lyricists;
}
public float getLength() {
return length;
}
public void setLength(float length) {
this.length = length;
}
/* public MoviesInfo getMoviesInfo() {
return moviesInfo;
}
#Access(AccessType.PROPERTY)
#ManyToOne
#JoinColumn(name = "movieId")
public void setMoviesInfo(MoviesInfo moviesInfo) {
this.moviesInfo = moviesInfo;
}*/
}
Please help me to write a model class where I can implement all the CRUD operations .
My expectaion of data in the table:
MovieId year Song name setofsingers lyricists length
CA1532 2015 adiga adiga hemachandra, roopa ram,sirivennala 5:46
If your expectation is a column you should not be using #ElementCollection as it will create one table each. (songs_info, songs_info_lyricists, songs_info_singers).
You can use a model like this, where you save a simple column but then retrieving a List and removing the get and set for simple singers and liricists:
The model:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "songs_info")
public class SongsInfo {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private int year;
#Column
private String singers;
#Column
private String lyricists;
private float length;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public List<String> getSingersList() {
if(this.singers != null){
return Arrays.asList(this.singers.split(","));
}else
return new ArrayList<String>();
}
public void setSingersList(List<String> singersList) {
this.singers = String.join(",", singersList);
}
public List<String> getLyricistsList() {
if(this.lyricists != null){
return Arrays.asList(this.lyricists.split(","));
}else
return new ArrayList<String>();
}
public void setLyricistsList(List<String> lyricistsList) {
this.lyricists = String.join(",", lyricistsList);
}
public float getLength() {
return length;
}
public void setLength(float length) {
this.length = length;
}
}
The repo :
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.web.bind.annotation.CrossOrigin;
import com.atos.travel.models.SongsInfo;
#RepositoryRestResource(collectionResourceRel = "songs", path = "songs")
#CrossOrigin("*")
public interface SongsRepository extends PagingAndSortingRepository<SongsInfo, Long> {
}
Then your post should be like that:
{ "length": "5.39", "lyricistsList": ["ram","Hemanth" ], "singersList": [ "hema","roopa" ], "year": 2005}
You can do it like this:
#SpringBootApplication
public class So45179111Application {
public static void main(String[] args) {
SpringApplication.run(So45179111Application.class, args);
}
public static class ListToCommaTextConverter implements AttributeConverter<List<String>, String> {
#Override
public String convertToDatabaseColumn(List<String> attribute) {
if (attribute == null || attribute.isEmpty()) {
return null;
}
return attribute.stream().collect(Collectors.joining(","));
}
#Override
public List<String> convertToEntityAttribute(String dbData) {
if (dbData == null || dbData.isEmpty()) {
return Collections.emptyList();
}
return Stream.of(dbData.split(",")).collect(Collectors.toList());
}
}
#Entity
#Table(name = "Movies")
public static class MoviesInfo extends AbstractPersistable<Long> {
private String name;
// getters and setters
}
#Entity
#Table(name = "Songs")
public static class SongsInfo extends AbstractPersistable<Long> {
#Column(columnDefinition = "text")
#Convert(converter = ListToCommaTextConverter.class)
private List<String> singers;
#Column(columnDefinition = "text")
#Convert(converter = ListToCommaTextConverter.class)
private List<String> lyricists;
#Column(columnDefinition = "numeric(3,2)")
private float length;
private int year;
#ManyToOne
#JoinColumn(name = "movie_id", foreignKey = #ForeignKey(name = "Songs_Movies_fk1"))
private MoviesInfo movie;
// getters and setters
}
#RestController
#RequestMapping("/songs")
public static class SongsApi {
private final SongsInfoRepository songsInfoRepository;
#Autowired
public SongsApi(SongsInfoRepository songsInfoRepository) {
this.songsInfoRepository = songsInfoRepository;
}
#PostMapping
public SongsInfo store(#RequestBody SongsInfo songsInfo) {
return songsInfoRepository.save(songsInfo);
}
}
}
interface SongsInfoRepository extends CrudRepository<So45179111Application.SongsInfo, Long> {}
And you can use your request:
$ curl -XPOST -H'Content-Type: application/json' -d '{ "length": 5.39, "lyricists": [ "ram","Hemanth" ], "singers": [ "hema","roopa" ], "year": 2005 }' localhost:8080/songs
{"id":3,"singers":["hema","roopa"],"lyricists":["ram","Hemanth"],"length":5.39,"year":2005,"movie":null,"new":false}%
Related
I have 3 tables in my database.FOOD_GROUP, FOOD_DESCRIPTION, SECONDARY_FOOD_DESCRIPTION,. My Application.java parses data files in the correct order so that on table can useful to the next. My issue is that when it comes to setting the databankID field in the SecondaryFoodDescription class, I get Data truncation: Data too long for column 'food' at row 1. This is surprising (at least to me) because, I use the same repository.save method to populate the foodGroup field in the class. Food.
EDIT: I'm aware that normally, an error like Data truncation: Data too long for column 'foodid' at row 1 would mean that what I'm trying to insert is too big for a field. However, I'm trying to insert an Entity. How could my Entity be too big?
Can anyone spot what I'm wrong?
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public CommandLineRunner demo(FoodGroupRepository foodGrouprepository, FoodRepository foodRepository,
SecondaryFoodInformationRepository secondaryFoodInformationRepository,
) {
return (args) -> {
FoodGroupParser foodGroupParser = new FoodGroupParser();
FoodGroup foodGroup;
Map<String, String> foodGroupMap = foodGroupParser.returnFoodGroupData();
for (Map.Entry<String, String> entry : foodGroupMap.entrySet()) {
foodGroup = new FoodGroup(entry.getKey(), entry.getValue());
foodGrouprepository.save(foodGroup);
}
FoodDescriptionParser foodDescriptionParser = new FoodDescriptionParser();
List<String[]> listOfFooods = foodDescriptionParser.returnFoodDescriptionData();
Food foodDescription;
SecondaryFoodDescription secondaryFoodDescription;
for (String[] foodItem : listOfFooods) {
foodDescription = new Food(foodItem[0],foodGrouprepository.findOne(foodItem[1]),foodItem[2],foodItem[4],foodItem[5],foodItem[8]);
secondaryFoodDescription = new SecondaryFoodDescription();
foodRepository.save(foodDescription);
secondaryFoodDescription = new SecondaryFoodDescription(foodRepository.findOne(foodItem[0]),foodItem[6],foodItem[7],foodItem[9],foodItem[10],foodItem[11]);
secondaryFoodInformationRepository.save(secondaryFoodDescription);
}
};
}
}
Repository interfaces:
#RepositoryRestResource
public interface FoodGroupRepository extends PagingAndSortingRepository<FoodGroup,String> {
}
#RepositoryRestResource
public interface FoodRepository extends PagingAndSortingRepository<Food, String>{
List<Food> findByLongDescription(#Param("longDescription") String name);
}
#RepositoryRestResource
public interface SecondaryFoodInformationRepository extends PagingAndSortingRepository<SecondaryFoodDescription,Long> {
}
Food group entity:
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
#Entity
#Table(name = "FOOD_GROUP")
public class FoodGroup implements Serializable{
public FoodGroup(){}
public FoodGroup(String id, String foodName){
this.setFoodGroupCode(id);
this.setFoodName(foodName);
}
#Id
#NotNull
#Size(min = 4,max = 4)
private String foodGroupCode;
#NotNull
#Size(max=60)
private String foodName;
public String getFoodGroupCode() {
return foodGroupCode;
}
public void setFoodGroupCode(String foodGroupCode) {
this.foodGroupCode = foodGroupCode;
}
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
}
Food entity:
import javax.persistence.*;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
#Entity
#Table(name = "FOOD_DESCRIPTION")
public class Food implements Serializable {
#Id
#NotNull()
#Column(name = "NUTRITION_DATA_BANK_ID")
#Size(min = 5, max = 5)
private String nutritionDatabankID;
private FoodGroup foodGroupID;
#OneToMany(mappedBy = "foodDescription", cascade = CascadeType.ALL)
private final Set<NutritionDataForSpecificFood> ndesc = new HashSet<>();
public Set<NutritionDataForSpecificFood> getNutrients() {
return Collections.unmodifiableSet(this.ndesc);
}
public void addFood(NutritionDataForSpecificFood specificFood) {
specificFood.setFoodDescription(this);
this.ndesc.add(specificFood);
}
#NotNull
#Column(name = "LONG_DESCRIPTION")
#Size(max = 200)
private String longDescription;
#Size(max = 100)
#Column(name = "COMMON_NAME")
private String commonName;
#Size(max = 65)
#Column(name = "MANUFACTURER_NAME")
private String manufacturerName;
#Size(max = 65)
#Column(name = "SCIENTIFIC_NAME")
private String scientificNameOfFood;
public Food() {
}
public Food(String nutritionDatabankID, FoodGroup foodGroupID, String longDescription, String commonName,
String manufacturerName, String scientificNameOfFood) {
this.nutritionDatabankID = nutritionDatabankID;
this.foodGroupID = foodGroupID;
this.longDescription = longDescription;
this.commonName = commonName;
this.manufacturerName = manufacturerName;
this.scientificNameOfFood = scientificNameOfFood;
}
public FoodGroup getFoodGroupID() {
return foodGroupID;
}
public void setFoodGroupID(FoodGroup foodGroupID) {
}
public String getNutritionDatabankID() {
return nutritionDatabankID;
}
public void setNutritionDatabankID(String nutritionDatabankID) {
this.nutritionDatabankID = nutritionDatabankID;
}
public String getLongDescription() {
return longDescription;
}
public void setLongDescription(String longDescription) {
this.longDescription = longDescription;
}
public String getCommonName() {
return commonName;
}
public void setCommonName(String commonName) {
this.commonName = commonName;
}
public String getManufacturerName() {
return manufacturerName;
}
public void setManufacturerName(String manufacturerName) {
this.manufacturerName = manufacturerName;
}
public String getScientificNameOfFood() {
return scientificNameOfFood;
}
public void setScientificNameOfFood(String scientificNameOfFood) {
this.scientificNameOfFood = scientificNameOfFood;
}
}
Secondary Food Info entity:
import javax.persistence.*;
import javax.validation.constraints.Size;
import java.io.Serializable;
#Entity
#Table(name = "SECONDARY_FOOD_DESCRIPTION")
public class SecondaryFoodDescription implements Serializable {
public SecondaryFoodDescription(){}
public SecondaryFoodDescription(Food food,String refuseDescription, String refusePercentage,
String caloriesFromProteinFactor, String caloriesFromFatFactor,
String caloriesFromCarbsFactor){
this.food = food;
this.refuseDescription = refuseDescription;
this.refuseDescription = refuseDescription;
this.caloriesFromProteinFactor = caloriesFromProteinFactor;
this.caloriesFromFatFactor = caloriesFromFatFactor;
this.caloriesFromCarbsFactor = caloriesFromCarbsFactor;
}
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
public Long getId() {
return id;
}
private Food food;
#Size(max = 135)
#Column(name = "REFUSE_DESCRIPTION")
private String refuseDescription;
#Column(name = "REFUSE_PERCENTAGE")
private String refusePercentage;
#Column(name = "CALORIES_FROM_PROTEIN_FACTOR")
private String caloriesFromProteinFactor;
#Column(name = "CALORIES_FROM_FAT_FACTOR")
private String caloriesFromFatFactor;
#Column(name = "CALORIES_FROM_CARBS_FACTOR")
private String caloriesFromCarbsFactor;
public String getRefuseDescription() {
return refuseDescription;
}
public void setRefuseDescription(String refuseDescription) {
this.refuseDescription = refuseDescription;
}
public String getRefusePercentage() {
return refusePercentage;
}
public void setRefusePercentage(String refusePercentage) {
this.refusePercentage = refusePercentage;
}
public String getCaloriesFromProteinFactor() {
return caloriesFromProteinFactor;
}
public void setCaloriesFromProteinFactor(String caloriesFromProteinFactor) {
this.caloriesFromProteinFactor = caloriesFromProteinFactor;
}
public String getCaloriesFromFatFactor() {
return caloriesFromFatFactor;
}
public void setCaloriesFromFatFactor(String caloriesFromFatFactor) {
this.caloriesFromFatFactor = caloriesFromFatFactor;
}
public String getCaloriesFromCarbsFactor() {
return caloriesFromCarbsFactor;
}
public void setCaloriesFromCarbsFactor(String caloriesFromCarbsFactor) {
this.caloriesFromCarbsFactor = caloriesFromCarbsFactor;
}
public Food getFood() {
return food;
}
public void setFood(Food food) {
this.food = food;
}
}
You need to annotate with appropriate relations #OneToOne/#OneToMany, otherwise the complete object will be serialized and persisted as a BLOB. If the BLOB size is large you get the Data Truncation error.
"Solved" by adding a #OneToOne annotation to the field ,food in the SecondaryFoodDescription entity.
This is an unsatisfying answer though. In the Food entity there is an unannotated field for the entity FoodGroup. That field does not produce the "column to long" error.
I am trying to perform join in hibernate and i am using struts2.
I am working with hibernate using annotaions. Now i am unable to perform join between two tables.My first table is "studentprojects" which contain pid and email.Second table is "initialprojectdetials" which contains pid,name,description... similarly some other fields.I have to get the data of second table by performing join around pid of first table.
For this am using this query:
String hql="from InitialProjectDTO I join I.projectId S where I.projectId=:id";
Query query=session.createQuery(hql);
query.setParameter("id", id);
mail =query.list();
where mail is the arraylist of InitialProjectDTO.
And my InitialProjectDTO is:
package edu.pma.dto;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;
#Entity
#Table(name="initialprojectdetail")
public class InitialProjectDTO {
#Id
#Column(name="projectId")
#OneToMany(cascade=CascadeType.ALL)
#JoinTable(name="studentprojects",joinColumns=#JoinColumn(name="projectId"))
int projectId;
#Column(name="name")
String name;
#Column(name="description")
String description;
#Column(name="technology")
String technology;
#Column(name="guide")
String guide;
#Column(name="duration")
int duration;
#Column(name="status")
String status;
#Column(name="report")
String report;
public String getReport() {
return report;
}
public void setReport(String report) {
this.report = report;
}
public int getProjectId() {
return projectId;
}
public void setProjectId(int projectId) {
this.projectId = projectId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getTechnology() {
return technology;
}
public void setTechnology(String technology) {
this.technology = technology;
}
public String getGuide() {
return guide;
}
public void setGuide(String guide) {
this.guide = guide;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
my SudentProjectDTO is:
package edu.pma.dto;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="studentprojects")
public class StudentProjectDTO {
public int getProjectId() {
return projectId;
}
public void setProjectId(int projectId) {
this.projectId = projectId;
}
#Id
#Column(name="email")
String email;
#Column(name="projectId")
int projectId;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
This is the error which i am getting:
Illegal attempt to map a non collection as a #OneToMany, #ManyToMany or #CollectionOfElements: edu.pma.dto.InitialProjectDTO.projectId
Method "execute" failed for object edu.pma.actions.LoginAction#1096a56
File: org/hibernate/cfg/annotations/CollectionBinder.java
You should try to use different models
#Entity
public class InitialProjectDTO {
#OneToMany(mappedBy = "project")
private Collection<StudentProjectDTO> students;
}
#Entity
public class StudentProjectDTO {
#ManyToOne
private InitialProjectDTO project;
}
And with the proper model it shuld be easy to write hql, you might want to look here for examples https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html.
Also I would suggest to look here for example of models. http://viralpatel.net/blogs/hibernate-one-to-many-annotation-tutorial/
See following example might its help to you
#Entity
#Table(name="initialprojectdetail")
public class InitialProjectDTO {
private Integer initialProjectDTOId;
private Set<StudentProjectDTO > studentProjectDTO = new HashSet<StudentProjectDTO >(0);
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "initial_projectDTO_id", unique = true, nullable = false)
public Integer getInitialProjectDTOId() {
return this.initialProjectDTOId;
}
public void setInitialProjectDTOId(Integer initialProjectDTOId) {
this.initialProjectDTOId = initialProjectDTOId;
}
#OneToMany(mappedBy = "studentprojects", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
public Set<StudentProjectDTO> getUserRole() {
return this.studentProjectDTO;
}
public void setUserRole(Set<StudentProjectDTO> studentProjectDTO) {
this.studentProjectDTO = studentProjectDTO;
}
}
#Entity
#Table(name="studentprojects")
public class StudentProjectDTO {
private InitialProjectDTO project;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "initial_projectDTO_id", nullable = false)
public User getProject() {
return this.project;
}
public void setProject(InitialProjectDTO project) {
this.project = project;
}
}
your Query shoud be something like this
String hql="SELECT ip from InitialProjectDTO ip JOIN ip.studentProjectDTO sp WHERE sp.projectId = :id";
Query query=session.createQuery(hql);
query.setParameter("id", id);
mail =query.list();
I have a problem that Hibernate is unable to determine the type for Collection at the table Region. I am trying to create a foreign key of table Actels through one-to-many relationship. a region can have many actels.
In detail:
The error I am getting is this:
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.Collection, at table: Region, for columns: [org.hibernate.mapping.Column(collection_actels)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:316)
at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:294)
at org.hibernate.mapping.Property.isValid(Property.java:238)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:469)
at org.hibernate.mapping.RootClass.validate(RootClass.java:270)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1294)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1742)
at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:94)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:905)
... 20 more
Region.java:
package com.springJPA.domain;
import java.io.Serializable;
import java.lang.String;
import java.util.Collection;
import javax.persistence.*;
import com.springJPA.domain.Actels;
/**
* Entity implementation class for Entity: Reseau
*
*/
#Entity
public class Region implements Serializable {
private int id_region;
private String region;
private String ville;
private int codep;
private int num_region;
private int num_ville;
private Collection<Actels> collection_actels;
private static final long serialVersionUID = 1L;
private Collection<Actels> actels;
public Region() {
super();
}
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "id_Sequence_region")
#SequenceGenerator(name = "id_Sequence_region", sequenceName = "ID_SEQ_REGION")
public int getId_region() {
return id_region;
}
public void setId_region(int id_region) {
this.id_region = id_region;
}
public String getRegion() {
return this.region;
}
public void setRegion(String region) {
this.region = region;
}
public String getVille() {
return this.ville;
}
public void setVille(String ville) {
this.ville = ville;
}
public int getCodep() {
return this.codep;
}
public void setCodep(int codep) {
this.codep = codep;
}
public Collection<Actels> getCollection_actels() {
return collection_actels;
}
public void setCollection_actels(Collection<Actels> collection_actels) {
this.collection_actels = collection_actels;
}
public int getNum_region() {
return num_region;
}
public void setNum_region(int num_region) {
this.num_region = num_region;
}
public int getNum_ville() {
return num_ville;
}
public void setNum_ville(int num_ville) {
this.num_ville = num_ville;
}
#OneToMany
#JoinColumn(name = "Region_id_region", referencedColumnName = "id_region")
public Collection<Actels> getActels() {
return actels;
}
public void setActels(Collection<Actels> param) {
this.actels = param;
}
}
Actels.java:
package com.springJPA.domain;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
/**
* Entity implementation class for Entity: Actels
*
*/
#Entity
public class Actels implements Serializable {
private int id_actels;
private String nomActels;
private int num_actel;
private Region region;
private static final long serialVersionUID = 1L;
public Actels() {
super();
}
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "id_Sequence_actels")
#SequenceGenerator(name = "id_Sequence_actels", sequenceName = "ID_SEQ_ACTELS")
public int getId_actels() {
return id_actels;
}
public void setId_actels(int id_actels) {
this.id_actels = id_actels;
}
public String getNomActels() {
return this.nomActels;
}
public void setNomActels(String nomActels) {
this.nomActels = nomActels;
}
public Region getRegion() {
return region;
}
public void setRegion(Region region) {
this.region = region;
}
public int getNum_actel() {
return num_actel;
}
public void setNum_actel(int num_actel) {
this.num_actel = num_actel;
}
}
Put any JPA annotation above each field instead of getter property:
#SequenceGenerator(name = "id_Sequence_actels", sequenceName = "ID_SEQ_ACTELS")
private int id_actels;
And:
#JoinColumn(name = "Region_id_region", referencedColumnName = "id_region")
private Collection<Actels> actels;
#Column
#ElementCollection(targetClass=String.class)
public Set<String> getOptions() {
return options;
}
OR
#OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "....")
public Set<String> getOptions() {
return options;
}
I read a CSV file with university courses (11000 rows) and want to insert this using hibernate into a MySQL database. The problem is that hibernate inserts each row twice. There is a bug regarding this issue. Solution should be to flush each child after persisting.
See here: Hibernate inserts duplicates into a #OneToMany collection and https://hibernate.atlassian.net/browse/HHH-6776
Here is the code from my Spring applications controller:
#RequestMapping(value = "/readcsv")
public String readCSVFile() {
entityManager.setFlushMode(FlushModeType.COMMIT);
//Load all courses and universities and add to hashmap for fast lookup.
ArrayList<Course> allCourses = (ArrayList) entityManager.createQuery("FROM Course").getResultList();
MultiValueMap courseMap = MultiValueMap.decorate(new HashMap<String, Course>());
for (Course c : allCourses) {
courseMap.put(c.getName(), c);
}
ArrayList<University> allUniversities = (ArrayList) entityManager.createQuery("FROM University").getResultList();
HashMap<String, University> universityMap = new HashMap<String, University>();
for (University u : allUniversities) {
universityMap.put(u.getName(), u);
}
Country sweden = (Country) entityManager.createQuery("SELECT c FROM Country c WHERE c.name = 'Sweden'").getSingleResult();
ArrayList<Course> coursesToAdd = new ArrayList<Course>();
//Set fixed parameters for this specific dataset.
String filename = "/Volumes/320gb/macbookpro/Documents/AntagningsstatistikVT2015.csv";
String semester = "Spring2015";
String type = "Classroom";
int pace = 100;
University u;
try {
//Read file
File fileDir = new File(filename);
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(fileDir), "UTF-8"));
String line;
//Loop lines in file
while ((line = in.readLine()) != null) {
// use comma as separator
String[] courseLine = line.split(",");
String name = courseLine[1];
String university = courseLine[2];
//See if the course is already in the database
Collection<Course> coursesWithSameName = courseMap.getCollection(name);
boolean found = false;
if (coursesWithSameName != null) {
for (Course course: coursesWithSameName) {
if (course.getUniversity().getName().equals(university)) {
System.out.println("COURSE ALREADY IN DB : " + name + " " + university);
found = true;
}
}
}
//If not, insert it
if (found == false) {
if (!universityMap.containsKey(university)) {
u = new University();
u.setName(university);
u.setCountry(sweden);
u.setUserAdded(false);
universityMap.put(u.getName(), u);
entityManager.persist(u);
entityManager.flush();
} else {
u = universityMap.get(university);
}
Course course = new Course();
course.setName(name);
course.setUniversity(u);
course.setType(type);
course.setPace(pace);
course.setSemester(semester);
courseMap.put(course.getName(), course);
coursesToAdd.add(course);
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("NUMBER OF ROWS IN coursesToAdd : " + coursesToAdd.size()); //Correctly prints 11000
for (Course course : coursesToAdd) { //inserts 22000 rows to db.
entityManager.persist(course);
entityManager.flush();
}
return "redirect:/";
}
Even though I flush after each persist double rows gets inserted. I have the #Transactional annotation on the controller class, is this the reason flush doesn't solve the problem? It however seems much slower when having flush there, which seems like it runs correctly compared to when it does a bulk insert on the end without flushing each insert.
Someone have a clue what is wrong in my code?
Thank you!
Edit:
Added University and Course entities:
Course:
import com.courseportal.project.account.Account;
import com.courseportal.project.utils.AbstractTimestampEntity;
import org.hibernate.annotations.Cascade;
import java.io.Serializable;
import java.util.List;
import javax.persistence.*;
#Entity
#Table(name="courses")
public class Course extends AbstractTimestampEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private long id;
#Column(name = "code")
private String code;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL)
#OrderBy("date desc")
private List<CourseRank> ranks;
#OneToMany(cascade = CascadeType.ALL)
#OrderBy("date desc")
private List<Comment> comments;
#OneToMany(cascade = CascadeType.ALL)
#OrderBy("date desc")
private List<UserBook> userBooks;
#ElementCollection
private List<CourseBook> courseBooks;
#ManyToMany
#JoinTable(name="courses_enrolledStudents")
private List<Account> enrolledStudents;
#OneToMany(cascade = CascadeType.ALL)
private List<Timeslot> times;
#ManyToOne
private University university;
/*
* fall
* spring
* summer
*/
private String semester;
private String teacherName;
private String courseLink;
private String requirementsLink;
/**
* Undergraduate
* Graduate
*/
private String level;
/**
* Online (MOOC - Massive Open Online Courses)
* Classroom (Traditional)
* Mixed (Lab)
*/
private String type;
/**
* How fast the course going in percentage.
*/
private int pace;
private int numberOfAssignments;
private int numberOfProjects;
private int numberOfExams;
private double credits;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public University getUniversity() {
return university;
}
public void setUniversity(University university) {
this.university = university;
}
public String getCourseLink() {
return courseLink;
}
public void setCourseLink(String courseLink) {
this.courseLink = courseLink;
}
public String getRequirementsLink() {
return requirementsLink;
}
public void setRequirementsLink(String requirementsLink) {
this.requirementsLink = requirementsLink;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public List<Timeslot> getTimes() {
return times;
}
public void setTimes(List<Timeslot> times) {
this.times = times;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getPace() {
return pace;
}
public void setPace(int pace) {
this.pace = pace;
}
public String getTeacherName() {
return teacherName;
}
public void setTeacherName(String teacherName) {
this.teacherName = teacherName;
}
public List<Comment> getComments() {
return comments;
}
public void setComments(List<Comment> comments) {
this.comments = comments;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<CourseRank> getRanks() {
return ranks;
}
public void setRanks(List<CourseRank> ranks) {
this.ranks = ranks;
}
public void addRank(CourseRank rank) {
this.ranks.add(rank);
}
public void removeRank(CourseRank rank) {
this.ranks.remove(rank);
}
public void addComment(Comment comment) {
comments.add(comment);
}
//User books, books that are for sale getters and setters
public void removeUserBook(UserBook book) {
this.userBooks.remove(book);
}
public void addUserBook(UserBook book) {
this.userBooks.add(book);
}
public List<UserBook> getUserBooks() {
return userBooks;
}
public void setUserBooks(List<UserBook> userBooks) {
this.userBooks = userBooks;
}
public List<CourseBook> getCourseBooks() {
return courseBooks;
}
public void setCourseBooks(List<CourseBook> courseBooks) {
this.courseBooks = courseBooks;
}
public void removeCourseBook(CourseBook book) {
this.courseBooks.remove(book);
}
public void addCourseBook(CourseBook book) {
this.courseBooks.add(book);
}
public int getNumberOfAssignments() {
return numberOfAssignments;
}
public void setNumberOfAssignments(int numberOfassignments) {
this.numberOfAssignments = numberOfassignments;
}
public int getNumberOfProjects() {
return numberOfProjects;
}
public void setNumberOfProjects(int numberOfprojects) {
this.numberOfProjects = numberOfprojects;
}
public int getNumberOfExams() {
return numberOfExams;
}
public void setNumberOfExams(int numberOfexams) {
this.numberOfExams = numberOfexams;
}
public double getCredits() {
return credits;
}
public void setCredits(double credits) {
this.credits = credits;
}
public List<Account> getEnrolledStudents() {
return enrolledStudents;
}
public void setEnrolledStudents(List<Account> enrolledStudents) {
this.enrolledStudents = enrolledStudents;
}
public void addEnrolledStudent(Account student) {
this.enrolledStudents.add(student);
}
public void removeEnrolledStudent(Account student) {
this.enrolledStudents.remove(student);
}
public String getSemester() {
return semester;
}
public void setSemester(String semester) {
this.semester = semester;
}
}
And University:
import com.courseportal.project.utils.AbstractTimestampEntity;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
#Entity
#Table(name="universities")
public class University extends AbstractTimestampEntity implements Serializable {
#Id
#GeneratedValue
private int id;
private String name;
#ManyToOne
private Country country;
private boolean userAdded;
public University() {}
public University(String university) {
name = university;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Country getCountry() {
return country;
}
public void setCountry(Country country) {
this.country = country;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isUserAdded() {
return userAdded;
}
public void setUserAdded(boolean userAdded) {
this.userAdded = userAdded;
}
}
Good day! I'm having a problem when updating an entity. When I click the "update" button, the changes are saved. However, when I go a different page, the recently changed (or added) items are there but the old items (that should be changed or removed) are also there. Particularly the relatedTags (the name and notes are updating just fine). Why is it not persistent or permanent? Here are the models:
Tag model:
package models;
import java.sql.Timestamp;
import java.util.*;
import javax.persistence.*;
import javax.validation.*;
import play.data.Form;
import play.data.validation.Constraints.*;
import play.db.ebean.*;
import play.db.ebean.Model.Finder;
import scala.Int;
#Entity
public class Tag extends Model{
#Id
private int id;
#Required
#MaxLength(value=100)
#Column(unique=true)
private String name;
#MaxLength(value=200)
private String notes;
#OneToMany(cascade=CascadeType.ALL)
public List<RelatedTag> relatedTags = new ArrayList<RelatedTag>();
#Version
public Timestamp lastUpdate;
public static Finder<Integer, Tag> find = new Finder(Int.class, Tag.class);
public Tag() {
}
public Tag(String name, String notes){
this.name = name;
this.notes = notes;
}
public Tag(int id, String name, String notes, List<RelatedTag> relatedTags) {
this.id = id;
this.name = name;
this.notes = notes;
this.relatedTags = relatedTags;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public List<RelatedTag> getRelatedTags() {
return relatedTags;
}
public void setRelatedTags(List<RelatedTag> relatedTags) {
this.relatedTags = relatedTags;
}
public static List<Tag> all() {
return find.all();
}
public static void create(Tag tag){
tag.save();
}
public static void delete(int id){
find.ref(id).delete();
}
public static void update(int id, Tag tag) {
tag.update(id); // updates this entity, by specifying the entity ID
}
public static boolean exists(Tag newTag) {
for(Tag allTags : Tag.find.all()) {
if(allTags.getName().equals(newTag.getName()))
return true;
}
return false;
}
}
RelatedTag model
package models;
import java.sql.Timestamp;
import java.util.*;
import javax.persistence.*;
import javax.validation.*;
import play.data.Form;
import play.data.validation.Constraints.*;
import play.db.ebean.*;
import play.db.ebean.Model.Finder;
import scala.Int;
#Entity
public class RelatedTag extends Model {
#Id
public int rtID;
private int id; //same as Tag's id
private String relationship;
private String relatedNotes;
#Version
public Timestamp lastUpdate;
public RelatedTag() {}
public RelatedTag(int id, String relationship, String relatedNotes) {
this.id = id;
this.relationship = relationship;
this.relatedNotes = relatedNotes;
}
public void setId(int id){
this.id = id;
}
public void setRelationship(String relationship){
this.relationship = relationship;
}
public void setRelatedNotes(String relatedNotes) {
this.relatedNotes = relatedNotes;
}
public int getId(){
return id;
}
public String getRelationship(){
return relationship;
}
public String getRelatedNotes() {
return relatedNotes;
}
public static void create(List<RelatedTag> rt){
((Model) rt).save();
}
public static boolean exists(String tagRelated) {
for(Tag tag : Tag.find.all()) {
if(tagRelated.equals(tag.getName()))
return true;
}
return false;
}
public static RelatedTag findByLabel(String tagRelated, String relation, String relatedNotes) {
RelatedTag relatedTag = null;
for(Tag tag : Tag.find.all()) {
if(tagRelated.equals(tag.getName())) {
relatedTag = new RelatedTag(tag.getId(), relation, relatedNotes);
}
}
return relatedTag;
}
public static Tag findTag(int id) {
for(Tag tag : Tag.find.all()) {
if(id == tag.getId())
return tag;
}
return null;
}
}
And here is where the updating happens.
Form<Tag> filledForm = tagForm.fill(Tag.find.byId(id)).bindFromRequest();
Tag editedTag = RelatedTag.findTag(id);
if(filledForm.hasErrors()) {
return badRequest(editTagForm.render(user, editedTag, filledForm, tags));
}
else {
List<RelatedTag> relatedTagsAlloc = new ArrayList<RelatedTag>();
java.util.Map<String, String[]> map = request().body().asFormUrlEncoded();
String[] relatedTags = map.get("relatedTags.tag.name");
String[] relationship = map.get("relatedTags.relationship");
String[] relatedNotes = map.get("relatedTags.relatedNotes");
if (relatedTags != null) {
for (int i = 0; i < relatedTags.length; i++) {
if (RelatedTag.exists(relatedTags[i].trim().toLowerCase().replaceAll("\\s+", " "))) {
relatedTagsAlloc.add(RelatedTag.findByLabel(
relatedTags[i].trim().toLowerCase().replaceAll("\\s+", " "), relationship[i], relatedNotes[i].trim()));
} else {
Tag unknown = new Tag(relatedTags[i], "");
Tag.create(unknown);
relatedTagsAlloc.add(RelatedTag.findByLabel(
relatedTags[i].trim().toLowerCase().replaceAll("\\s+", " "), relationship[i], relatedNotes[i].trim()));
}
}
editedTag.getRelatedTags().clear();
}
editedTag.setName(filledForm.get().getName().toLowerCase().replaceAll("\\s+", " "));
editedTag.setRelatedTags(relatedTagsAlloc);
editedTag.update();
Application.log(user, editedTag, action);
writeToFile(editedTag);
return ok(summary.render(user, editedTag));
}
What have I been doing wrong? Please help me fix this I really need your guidance. Thank you very much!