Spring + MySQL records being duplicated instead of updated - java

In my project I'm migrating data from SQLite db into MySQL db. The problem is that when I migrate multiple times, the already existing records are being duplicated, instead of being updated(in case there are any changes in SQLite, otherwise no action is expected).
I'm using JpaRepository.saveAll(), which internally invokes save() for each entity:
#Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}
Then isNew() is invoked but I'm not sure how it compares records in the target db to the ones that are currently being migrated and if that's where the problem occurs at all.
Do you have any suggestions to solving my problem?
That's my entity:
#Entity
#Table(name = "movies")
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
-- properties
-- constructors/getters/setters/equals/hashcode
}
That's the service that migrates movies(SQLiteMovie is the entity extracted from SQLite, which is then mapped to (MySQL)Movie):
#Service
public class MovieMigrationServiceImpl implements MovieMigrationService {
private static final int MOVIE_TYPE = 1;
private final MovieMapper movieMapper;
private final MovieService movieService;
private final SQLiteMovieService sqliteMovieService;
public MovieMigrationServiceImpl(MovieMapper movieMapper, MovieService movieService,
SQLiteMovieService sqliteMovieService) {
this.movieMapper = movieMapper;
this.movieService = movieService;
this.sqliteMovieService = sqliteMovieService;
}
#Override
public String migrateMovies() {
Collection<SQLiteMovie> sqliteMovies = sqliteMovieService.getAllByMetadataType(MOVIE_TYPE);
Collection<Movie> movies = mapMovies(sqliteMovies);
movieService.saveMovies(movies);
return movies.size() + " movies migrated successfully!";
}
private Collection<Movie> mapMovies(Collection<SQLiteMovie> sqliteMovies) {
return sqliteMovies.stream()
.map(movieMapper::toMovie)
.collect(Collectors.toList());
}
}
And finally that's the structure of the table in MySQL:
Thanks in advance!

Related

Hibernate is not using the proxy of a session scope bean spring

I'm using Spring #Scope(value = "session", proxyMode=ScopedProxyMode.TARGET_CLASS) beans for objects that should be shared across a single Http-Session. This will provide for example one "Project" object for each User who is using my application.
To get this working I had to implement an interceptor for Hibernate that is returning the name of the class:
public class EntityProxySupportHibernateInterceptor extends EmptyInterceptor {
private static final long serialVersionUID = 7470168733867103334L;
#Override
public String getEntityName(Object object) {
return AopUtils.getTargetClass(object).getName();
}
}
With this interceptor I can use a Spring CrudRepository to save a Project-entity in the database:
#Repository
public interface ProjectRepository extends CrudRepository<Project, Integer> {
Project findByProjectId(int projectId);
}
Project-entity:
#Component
#Entity
#Table(name = "xxx.projects")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class Project implements Serializable {
private static final long serialVersionUID = -8071542032564334337L;
private int projectId;
private int projectType;
#Id
#Column(name = "project_id")
public int getProjectId() {
return projectId;
}
public void setProjectId(int projectId) {
this.projectId = projectId;
}
#Column(name = "project_type")
public int getProjectType() {
return projectType;
}
public void setProjectType(int projectType) {
this.projectType = projectType;
}
}
Storing the Project in the database works as expected. I can have a look at the database and the correct values are inserted. Now I have a different entity that I'm creating the same way as the project and that I want to save in the database via a CrudRepository.
Here the problem begins. Hibernate is not inserting the values that I have set. Hibernate always only inserts null into the database. Reading the values in my Spring application is working as expected. I think that Hibernate is not using the proxy of the entity but the underlying blueprint of the object. How can I force Hibernate to use the proxy with the correct values?
Repository:
#Repository("DataInput001Repository")
public interface DataInputRepository extends CrudRepository<DataInput, DataInputId> {}
Entity:
#Component("DataInput001")
#Entity
#Table(name = "xx.data_input_001")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#IdClass(DatanputId.class)
public class DataInput implements Serializable {
private static final long serialVersionUID = -6941087210396795612L;
#Id
#Column(name = "project_id")
private int projectId;
#Column(name = "income")
private String income;
#Column(name = "income_increase")
private String incomeIncrease;
/* Getter + Setter */
}
Service:
#Service("DataInputService001")
public class DataInputServiceImpl implements DataInputService {
#Resource(name = "DataInputMapper001")
DataInputMapperImpl dataInputMapper;
#Resource(name = "DataInput001Repository")
DataInputRepository dataInputRepository;
#Resource(name = "DataInput001")
DataInput datanInput;
#Transactional
public void createDataInput(String json) throws Exception {
dataInputMapper.mapDataInput(json);
dataInputRepository.save(dataInput);
}
public DataInput getDataInput() {
return dataInput;
}
public void setDataInput(DataInput dataInput) {
this.dataInput = dataInput;
}
}

Spring boot CrudRepository saves bad data

i have problem with saving data in DB.I'm new in Spring Boot. When i run my program the result of writen data is: packagename#randomcode example:com.abc.patient.Patient#6e3e681e
This is my Entity class - Patient.java
#Entity
public class Patient {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
// getter, setter, constructor, etc
}
This is my CrudRepo PatientRepository.java
public interface PatientRepository extends CrudRepository<Patient,Integer> {
}
This is my Service class PatientService.java
#Service
public class PatientService {
#Autowired
private PatientRepository patientRepository;
public void savePatient (String name) {
Patient patient = new Patient(name);
patientRepository.save(patient);
}
public Optional<Patient> showPatient(int id) {
return patientRepository.findById(id);
}
public List<Patient> showAllPatients() {
List<Patient> patients = new ArrayList<>();
patientRepository.findAll().forEach(patients::add);
return patients;
}
}
I think that problem in in the savePatient method in this line:
Patient patients = new Patient(name);
I checked the "name" parameter and it's in 100% correct String. I'm using Derby DB.
The only problem you have is how you are printing out your Patient class. Define a proper toString() or just debug yourself to see the resulting fields. There is no problem in your JPA implementation.
See this question for the details of default toString
Try:
public void savePatient(Patient patient) {
patientRepository.save(patient);
}

Why ResponseEntity calls hibernate while lazy loading active

Below is the DAO. I am getting the first UppeningUsers object. Note that here for this function I do not want to return peopleWhoBlockedMe set which is located inside the UppeningUsers..
But in different functions I would like to return that information. Note that Both of them are LAZY fetching. With evict I tried to detach the object but still it did not work.
First of all RESTcontroller is below. Then the DAO code is below. Then two entity descriptions are below.
Question is: I see that until
return new ResponseEntity(returned, HttpStatus.OK);
There is only one query which is the typical select. I do not want hibernate to go and take also UserBlock information of that specific UppeningUser. Because it is not needed for this service response. However even though it is lazy loading for some reason
return new ResponseEntity(returned, HttpStatus.OK);
calls the hibernate. I dont know why in restcontroller still it is connected to the database. I tried evict but didnt work.
The json response is
{"id":7,"peopleWhoBlockedMe":[{"blockedId":7}]}
But I do not want for this function to return this peopleWhoBlockedMe. It can be empty.
PLEASE NOTE that in other service for example I will explictly request this peopleWhoBlockedMe but just for this business logic I do not need this information. So what I can do to prevent this so whenever I actually want to call peopleWhoBlockedMe I can get it. Not automaticly.
#RestController
public class TempController {
#Autowired
UppeningUsersService uppeningUsersService;
#RequestMapping(value = "/testing", method = RequestMethod.GET)
public ResponseEntity<UppeningUsers> getPhotos() {
try {
UppeningUsers returned = uppeningUsersService.getUsersDetailsPartial();
return new ResponseEntity<UppeningUsers>(returned, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
This part is the DAO.
#Repository
public class UppeningUsersDAO {
#Autowired
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf) {
this.sessionFactory = sf;
}
/**
* Get Existing user. Return error if there is not.
* #param incomingUser user who requested access.
* #return returns the guy information. All information.
*/
#Transactional
public UppeningUsers getUserDetails() throws Exception {
Session session = this.sessionFactory.getCurrentSession();
Query query = session.createQuery("from UppeningUsers ");
UppeningUsers returning = (UppeningUsers) query.list().get(0);
session.evict(returning);
return returning;
}
}
The main table is this one..
#Entity
#Table(name = "uppening_users")
#Proxy(lazy = true)
public class UppeningUsers {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private
int id;
#OneToMany(mappedBy = "blockedId",cascade =CascadeType.ALL, fetch = FetchType.LAZY)
private Set<UserBlocks> peopleWhoBlockedMe;
public UppeningUsers() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Set<UserBlocks> getPeopleWhoBlockedMe() {
return peopleWhoBlockedMe;
}
public void setPeopleWhoBlockedMe(Set<UserBlocks> peopleWhoBlockedMes) {
this.peopleWhoBlockedMe = peopleWhoBlockedMes;
}
}
Now here is the other table.
#Entity
#Table(name="user_blocks")
#Proxy(lazy = true)
public class UserBlocks {
#Id
#Column(name="id")
#GeneratedValue(strategy= GenerationType.IDENTITY)
int id;
#Column(name = "blocked_id",insertable = false,updatable = false)
private int blockedId;
public int getBlockedId() {
return blockedId;
}
public void setBlockedId(int blockedId) {
this.blockedId = blockedId;
}
}
UPDATE: 2 forgot to add the service
#Service("uppeningUserService")
public class UppeningUsersService {
#Autowired
UppeningUsersDAO uppeningUsersDAO;
public UppeningUsers getUsersDetailsPartial( ) throws Exception {
return uppeningUsersDAO.getUserDetails();
}
}
Jens is right about her sentence. The layer methodology and writing business objects fix the issue. Thank you.

Associated Entity does not get saved in transaction

I am fairly new to entity frameworks, and don't understand why my entity does not get saved correctly.
edit:
I will try to out this test code soon, and see if it works:
#Transactional
public void doConvert(String lakoKod) {
LsZFutelszHavi futelszHavi = new LsZFutelszHavi();
//Give it values like:
futelszHavi.setOrigKorrOssz(new BigDecimal(500));
LsTLako lako = lsTLakoRepository.findOneByLakoKod(lakoKod);
LsZFutelsz futelsz = lsZFutelszRepository.findOneByLsTLako(lako);
//The modification which not get saved into tha Firebird table in the original code. Will it be saved now?
futelsz.setSzezVegenBefOssz(new BigDecimal(100));
futelszHavi.setLsZFutelsz(futelsz);
lsZFutelszHaviRepository.save(futelszHavi);
}
I am trying to convert from an old DBASE database, to a Firebird database. The Firebird database have it's tables mapped by entities. I read a DBASE table, then convert it row by row.
I use #Transactional, to either save all converted entities of the DBASE table, or save none.
I managed to convert every table correctly into the new database except one.
In the other tables, I only had to save one entity per record, and did not have to modify other type of entities. I usually had to create a Type X entity, connect it to a Type Y entity, then save. For save, I use a repository for the entity (an org.springframework.data.repository.PagingAndSortingRepository if it matters)
In that particular DBASE table, I have to create one entity of Type A, connect it to a Type B entity, modify the Type B entity, then save. The problem is, that Type B entity modification does not get saved into the Firebird table. Again, I use a repository to save the entity.
To get the Type B entity, I use it's repository's method:
LsZFutelsz findOneByLsTLako(LsTLako lsTLako);
I guessed, that maybe if I save this Type B entity with it's own repository, it would get modified in the database correctly. It did not help.
Please tell me if additional information is needed.
I copy my slightly altered code here (removed some logging, added some comments). LsZFutelszHavi is the Type A EntityClass, LsZFutelsz is type B EntityClass.
The Konverter abstract class. It is inherited for every DBASE table
public abstract class Konverter<RepositoryType extends CrudRepository<EntityType,Integer>, EntityType> {
protected String dbfPath;
protected DBaseTable sourceTable = null;
protected Logger logger;
protected RepositoryType repository;
protected String dBaseEncoding = DBaseTable.CP852;
public Konverter(String dbfPath, Logger logger, RepositoryType repository) {
this.dbfPath = dbfPath;
this.logger = logger;
this.repository = repository;
}
/*
This method should be called, to start converting
*/
#Transactional
public void konvert() {
try {
/*It loads the DBASE database*/
File sourceFile = new File(fileName);
sourceTable = new DBaseTable(sourceFile, dBaseEncoding);
sourceTable.open(IfNonExistent.ERROR);
Iterator<Record> recordIterator = sourceTable.recordIterator();
int count = 0;
try {
/*Converts the database table row by row*/
count = konvertSorok(recordIterator);
} catch (Exception e) {
throw e;
} finally {
sourceTable.close();
}
}
catch (CorruptedTableException | IOException | RuntimeException e) {
logger.error(QsLoggerUtils.getStackTraceString(e));//e.printStackTrace();
}
}
private int konvertSorok(Iterator<Record> recordIterator) {
int count = 0;
/*Converts the database table row by row*/
while(recordIterator.hasNext())
{
Record record = recordIterator.next();
/* Converting one row */
List<EntityType> entityIterable = konvertToEntity( record );
for (EntityType entityType : entityIterable) {
repository.save(entityType);
}
count++;
}
return count;
}
/**
* This should be implemented in the child method
* #param record
* #return
*/
protected abstract List<EntityType> konvertToEntity(Record record);
}
The child Class, that implmenets konvertToEntity method.
public class Konvert14FutelszHavi extends Konverter<LsZFutelszHaviRepository,LsZFutelszHavi> {
private static Logger logger = LoggerFactory.getLogger(Konvert12Futalany.class);
LsZFutelszHaviRepository lsZFutelszHaviRepository;
LsTLakoRepository lsTLakoRepository;
LsZFutelszRepository lsZFutelszRepository;
LsTEvhoRepository lsTEvhoRepository;
#Autowired
public Konvert14FutelszHavi(LsZFutelszHaviRepository lsZFutelszHaviRepository,
LsTLakoRepository lsTLakoRepository,
LsZFutelszRepository lsZFutelszRepository,
LsTEvhoRepository lsTEvhoRepository) throws IOException {
super(DBaseTable.chkFile(AppKonvertLax.PATH_LSZBF, AppKonvertLax.SOURCE_FILE_FUTELSZ_HAVI), logger, lsZFutelszHaviRepository);
dBaseEncoding = DBaseTable.CP1250;
this.lsTLakoRepository = lsTLakoRepository;
this.lsZFutelszHaviRepository = lsZFutelszHaviRepository;
this.lsZFutelszRepository = lsZFutelszRepository;
this.lsTEvhoRepository = lsTEvhoRepository;
}
#Override
protected List<LsZFutelszHavi> konvertToEntity(Record record) {
String ukod = record.getStringValue("UKOD").substring(1).trim();
BigDecimal ekaptam = new BigDecimal(record.getNumberValue("EKAPTAM").toString());
BigDecimal efutkul = new BigDecimal(record.getNumberValue("EFUTKUL").toString());
ArrayList<LsZFutelszHavi> returnArray = new ArrayList<LsZFutelszHavi>();
LsTLako lsTLako = lsTLakoRepository.findOneByLakoKod(ukod);
LsZFutelsz lsZFutelsz = lsZFutelszRepository.findOneByLsTLako(lsTLako);
if (lsZFutelsz == null) {
return returnArray;
}
/* Here is the modification in the lsZFutelsz (Type B) entity */
lsZFutelsz.setSzezVegenBefOssz(ekaptam);
/* From 10th month to 4th */
for (int i=10; i!=5; i++) {
if (i==13) {
i = 1;
}
String keyNumber = Integer.toString(i);
if (keyNumber.length() == 1) {
keyNumber = "0" + keyNumber;
}
BigDecimal fk = new BigDecimal(record.getNumberValue("FK_"+keyNumber).toString());
LsZFutelszHavi lsZFutelszHavi = new LsZFutelszHavi();
LsTEvho lsTEvho = lsTEvhoRepository.findOneByEvAndHo(2014, i);
lsZFutelszHavi.setLsTEvho(lsTEvho);
lsZFutelszHavi.setFizKorrOssz(fk);
lsZFutelszHavi.setOrigKorrOssz(efutkul);
/* This should be enough to save the lsZFutelsz entity modification I would think */
lsZFutelszHavi.setLsZFutelsz(lsZFutelsz);
returnArray.add(lsZFutelszHavi);
}
/* Even this does not help */
lsZFutelszRepository.save(lsZFutelsz);
return returnArray;
}
}
Repository for the Type B entity
#RepositoryRestResource(collectionResourceRel = LsZFutelszHavi.VERB_FUTELSZ, path = LsZFutelszHavi.VERB_FUTELSZ)
public interface LsZFutelszRepository extends PagingAndSortingRepository<LsZFutelsz, Integer> {
/*-----------------------------------------------------------------------------------------------*/
#RestResource(exported=false)
#Modifying
#Query(value="DELETE FROM ls_z_futelsz f WHERE f.lako_id = ?1", nativeQuery=true)
void deleteByLako(Integer integer);
/*-----------------------------------------------------------------------------------------------*/
LsZFutelsz findOneByLsTLako(LsTLako lsTLako);
}
Repository for the Type A entity
#RepositoryRestResource(collectionResourceRel = LsZFutelsz.VERB_FUTELSZHAVI, path = LsZFutelsz.VERB_FUTELSZHAVI)
public interface LsZFutelszHaviRepository extends PagingAndSortingRepository<LsZFutelszHavi, Integer> {
}
Entity Type A
#Entity
#Table(name="LS_Z_FUTELSZ_HAVI")
#NamedQuery(name="LsZFutelszHavi.findAll", query="SELECT l FROM LsZFutelszHavi l")
public class LsZFutelszHavi extends Audit implements Serializable {
public static final String VERB_FUTELSZ = "futelszamolasok";
/*-----------------------------------------------------------------------------------------------*/
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="GenFutelszHaviID", sequenceName="GEN_LS_Z_FUTELSZ_HAVI_ID", allocationSize= 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="GenFutelszHaviID")
#Column(name="HAVI_ELSZ_ID")
private Integer haviElszId;
#NotNull
#Column(name="FIZ_KORR_OSSZ")
private BigDecimal fizKorrOssz;
#NotNull
#Column(name="ORIG_KORR_OSSZ")
private BigDecimal origKorrOssz;
//uni-directional many-to-one association to LsFSzlafej
#ManyToOne
#JoinColumn(name="SZLA_ID")
private LsFSzlafej lsFSzlafej;
//uni-directional many-to-one association to LsTEvho
#ManyToOne
#JoinColumns({
#JoinColumn(name="EV", referencedColumnName="EV"),
#JoinColumn(name="HO", referencedColumnName="HO")
})
private LsTEvho lsTEvho;
//bi-directional many-to-one association to LsZFutelsz
#ManyToOne
#JoinColumn(name="ELSZ_ID")
private LsZFutelsz lsZFutelsz;
public LsZFutelszHavi() {
}
//[... setters getters ...]
}
Entity Type B
#Entity
#Table(name="LS_Z_FUTELSZ")
#NamedQuery(name="LsZFutelsz.findAll", query="SELECT l FROM LsZFutelsz l")
public class LsZFutelsz extends Audit implements Serializable {
public static final String VERB_FUTELSZHAVI = "futelszhavi";
/*-----------------------------------------------------------------------------------------------*/
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="GenFutelszID", sequenceName="GEN_LS_Z_FUTELSZ_ID", allocationSize= 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="GenFutelszID")
#Column(name="ELSZ_ID")
private Integer elszId;
#NotNull
#Column(name="LEOLV_FOGY_GJ")
private BigDecimal leolvFogyGj = BigDecimal.ZERO;
#NotNull
#Column(name="LEOLV_FOGY_OSSZ")
private BigDecimal leolvFogyOssz = BigDecimal.ZERO;
#NotNull
#Column(name="ELOZ_SZEZ_OSSZ")
private BigDecimal elozSzezOssz = BigDecimal.ZERO;
#NotNull
#Column(name="SZEZ_VEGEN_BEF_OSSZ")
private BigDecimal szezVegenBefOssz = BigDecimal.ZERO;
#NotNull
#Column(name="SZOSZT_UTAN_FENNM")
private BigDecimal szosztUtanFennm = BigDecimal.ZERO;
#NotNull
#Column(name="SZOSZTANDO_KULONB")
private BigDecimal szosztandoKulonb = BigDecimal.ZERO;
//uni-directional many-to-one association to LsTLakok
#ManyToOne
#JoinColumn(name="LAKO_ID")
private LsTLako lsTLako;
//bi-directional many-to-one association to LsZFutelszHavi
#OneToMany(mappedBy="lsZFutelsz", cascade={CascadeType.REMOVE})
private List<LsZFutelszHavi> lsZFutelszHaviTetelek;
public LsZFutelsz() {
}
//[... setters getters ...]
}
The code works, The field is just always happen to be the default value.
After testing it with simple methods, the other entity got saved into the database as well. Then I put a breakpoint into the original code which only get breaked, if the new value for the entity is different than the default. The program converted everything without breaking at the breakpoint.
So, I looked up which database I converting, and see what the contents ar. To my surprise it was always zero, the default value.
It's awkward. I was so sure, I misunderstood something, and coded wrong.

Issue With cache Abstraction using redis

I have a Problem with Spring - Data - Redis
I am using redis as cache for entities that i am fetching from database using JPA Repository.
Here is the entity structure :
#Entity(name = "issue_category")
public class IssueCategory extends AuditablePersistable {
private static final long serialVersionUID = 8659360844089652714L;
private Integer parentId;
private String name;
private String displayNameCRM;
private String displayNameSelfServe;
private List<IssueCategoryRule> issueCategoryRules = new ArrayList<>();
public static final Integer ROOT_PARENT_ID = 0;
// with all getter setters
}
Here is part where i am caching it:
#Override
#Cacheable(value = IC_CACHE, key = "#id")
public IssueCategory getIssueCategoriesById(Integer id) {
return issueCategoriesRepo.findById(id);
}
The Repo code for getting from db:
public interface IssueCategoryRepo extends JpaRepository<IssueCategory, Integer>
{
public IssueCategory findByName(#Param("name") String name);
public List<IssueCategory> findByParentId(#Param("parentId") Integer parentId);
public IssueCategory findById(#Param("id") Integer id);
}
The problem I am getting is
Class Cast exception when this entity is being extracted from cache
It says :
Can not cast from java.util.ArrayList to IssueCategory class.
Can anybody help.

Categories

Resources