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.
Related
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!
I have an SQL table:
#Table(name = "population_table")
public class Population {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String country;
private String state;
private String area;
private String population;
}
I want to get a count, grouping by country and state with the output class being List of Count:
private static class Count {
private String country;
private String state;
private long count;
}
I know the query is
SELECT country, state, Count(*)
FROM population_table
GROUP BY country, state
But I want to do this using JPA Specification. How can I achieve this using JPA Specification in spring boot?
You could achieve this by using Spring Data JPA Projections in Spring Data JPA.
Create a custom Repository method like
#Repository
public interface PopulationRepository extends JpaRepository<Population, Long> {
#Query("select new com.example.Count(country, state, count(p) )
from Population p
group by p.country, p.state")
public List<Count> getCountByCountryAndState();
}
Also you must define the specific constructor in Count class which will handle this projection
private static class Count {
private String country;
private String state;
private long count;
//This constructor will be used by Spring Data JPA
//for creating this class instances as per result set
public Count(String country,String state, long count){
this.country = country;
this.state = state;
this.count = count;
}
}
You can use JpaRepository interface.
Example:
#Repository
public interface PopulationRepository extends JpaRepository<Population, Long> {
public int countAllByCountryAndState(String countryName, String stateName);
}
And in your service:
#Service
#Transactional
public class PopulationService {
#Autowired
private PopulationRepository populationRepository;
public int countPopulationByCountryAndState(String countryName, String stateName) {
return populationRepository.countAllByCountryAndState(countryName, stateName);
}
}
Sorry, I made mistake it can be simpler. I edited my code.
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;
}
}
I'm using Neo4j to create graphs. The below codes is an example for spring data Neo4j. I can save a node entity when no id property value is provided.
But how to save a node entiry with a specific id property value?
Model Class:
#Data
#NodeEntity
public class Person {
#Id
#GeneratedValue
private Long id;
private String name;
private String title;
#Relationship(type = "ACTED_IN")
private List<Movie> movies = new ArrayList<>();
}
Repository Class
public interface PersonRepository extends Neo4jRepository<Person, Long> {
#Query("MATCH (n:Person {name:{name}}) RETURN n")
List<Person> findByName(#Param("name") String name);
}
Controller Class
#RestController
#RequestMapping("/person")
public class PersonController {
#Autowired
private PersonRepository personRepository;
#PostMapping("/save")
public Map save(#RequestBody Person person) {
Map resultMap = new HashMap();
String code = "200";
String msg = "success";
// It can save success when no id property value is provided
Person savedPerson = personRepository.save(person);
resultMap.put("code", code);
resultMap.put("msg", msg);
resultMap.put("data", savedPerson);
return resultMap;
}
}
I have tried it successfully and can be easily done provide the "id" should be
String not Long
Domain/DAO class:
#Id
#GeneratedValue(strategy = Neo4JCustomIdStrategy.class)
String id;
Repository Class:
#Repository
public interface PersonRepository extends Neo4jRepository<Person, String>{
}
And lastly, custom implementation of Strategy:
public class Neo4JCustomIdStrategy implements IdStrategy {
#Override
public Object generateId(Object entity) {
return String.valueOf(entity.hashCode());
}
}
The library I am using is spring-data-neo4j
I have a customer's database that has a collection, in which the document fields can vary between each other. There are some constant fields I can rely on, but as for the rest - I have no way of narrowing the field list as the customer wants the solution to be dynamic.
My question is - can I somehow implement a generic mapping that would return, let's say, a map of document's fields using Spring Data?
edit:
Thanks for the tips. I've tried getting the generic Object (hoping I'd be able to convert it into a map) using the entity:
#Document(collection = "Data")
public class DataEntity {
#Id
private String id;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
but fetching the object by the ID using MongoRepository produces an object with data field set to null.
I'm using SpringBoot 1.3.1.RELEASE with spring-boot-starter-data-mongodb 1.3.1.RELEASE.
You can use a Map for dynamic properties like below. Is this what you are looking for?
#Document(collection = "computers")
public class Computer {
#Id
private String id;
#Field("name")
private String name;
//Other constant fields
#Field("properties")
private Map<String, Object> properties;
}