Our Mapped Super class has createdBy column which is defined to be lazily loaded
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class AbstractAuditingEntity implements Serializable {
#CreatedBy
#ManyToOne(fetch = FetchType.LAZY)
#XmlTransient
#JoinColumn(name = "created_by", updatable = false, columnDefinition = "bigint")
protected User createdBy;
public User getCreatedBy() {
return createdBy;
}
public void setCreatedBy(User createdBy) {
this.createdBy = createdBy;
}
I need to load this property eagerly in one of the sub class that inherits this aforementioned class.
#Override
#XmlElement(name = "createdBy")
#JsonProperty("createdBy")
public User getCreatedBy() {
return super.getCreatedBy();
}
How can I do that?
I tried the following (used NamedEntityGraph and HQL), but, both did not return createdBy from MappedSuperClass that is defined as lazy
//defined at the top of Model
#NamedEntityGraphs({
// eagerly fetches created by and program names when used
#NamedEntityGraph(
name = "graphWithCreatedBy",
attributeNodes = {
#NamedAttributeNode("createdBy")
}
)
})
//Repository method
#EntityGraph(value = "Program.createdBy", type = EntityGraph.EntityGraphType.FETCH) //tried both LOAD and FETCH
Program findOne(Specification<Program> specification);
---Using HQL FETCH JOIN --
//Repository Implementation
private static final String PROGRAM_USER_QUERY = "SELECT " +
" sp FROM Program sp " +
" LEFT JOIN FETCH sp.createdBy where sp.id = :id";
Query query = entityManager.createQuery(PROGRAM_USER_QUERY ).
setParameter("id", id);
query.getSingleResult();
Both approaches returns program, but not the createdBy User
What am I doing wrong?
Related
public class ChallengeDto {
private Long id;
private Category category;
private String title;
private String subTitle;
private boolean like;
private int totalScore;
private int requiredScore;
public ChallengeDto(Long id, Category category, String title, String subTitle, boolean like, int totalScore, int requiredScore) {
this.id = id;
this.category = category;
this.title = title;
this.subTitle = subTitle;
this.like = like;
this.totalScore = totalScore;
this.requiredScore = requiredScore;
}
}
I created challengeDto that include challenge's properties(id, category, title, subtitle, totalScore, requiredScore) and like property(can know that if i like challenge or not).
If I put like button, that information stored challengeLike table.
public class ChallengeLike {
#Id
#GeneratedValue
#Column(name = "challenge_like_id")
private Long id;
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "challenge_id")
private Challenge challenge;
private LocalDateTime createDate;
}
Now I'm trying to write a code to retrieve challengeDto that checks if I clicked like or not, but I'm having a problem... I can't think of what kind of code to make.
#Repository
#RequiredArgsConstructor
public class ChallengeDtoRepository {
private final EntityManager em;
#Transactional
public List<ChallengeDto> findChallenges(Long userId) {
return em.createQuery(
"select new " +
"com.example.candy.controller.challenge.ChallengeDto(c.id,c.category,c.title,c.subTitle,????,c.totalScore,c.requiredScore)" +
" from Challenge c" +
" left join ChallengeLike cl on c.id = cl.challenge.id" +
" and cl.user.id = : userId", ChallengeDto.class)
.setParameter("userId", userId)
.getResultList();
}
}
try to rename the field to likeDone or something different than like, it makes the code ambiguous.
However, just simply do:
cl.likeDone
which means:
return em.createQuery(
"select new " +
"com.example.random.demo.dto.ChallengeDto(c.id,c.category,c.title,c.subTitle,cl.likeDone,c.totalScore,c.requiredScore)" +
" from Challenge c" +
" left join ChallengeLike cl on c.id = cl.challenge.id" +
" where cl.user.id = : userId", ChallengeDto.class)
.setParameter("userId", userId)
.getResultList();
However, try to use JPA if you don't have any mandatory condition to use native query or jpql.
JPA implementation:
#Repository
public interface ChallengeLikeRepository extends JpaRepository<ChallengeLike, Long> {
List<ChallengeLike> findAllByUser_Id(long userId);
}
Just call the repository method from service layer and map to your required dto:
public List<ChallengeDto> findChallenges(Long userId) {
List<ChallengeLike> entities = this.repository.findAllByUser_Id(userId);
return entities.stream().map(this::mapToDto).collect(Collectors.toList());
}
The mapToDto() method converts the entity to corresponding ChallengeDto
private ChallengeDto mapToDto(ChallengeLike x) {
return ChallengeDto.builder()
.category(x.getChallenge().getCategory())
.id(x.getChallenge().getId())
.like(x.isLikeDone())
.requiredScore(x.getChallenge().getRequiredScore())
.subTitle(x.getChallenge().getSubTitle())
.title(x.getChallenge().getTitle())
.totalScore(x.getChallenge().getTotalScore())
.userId(x.getUser().getId())
.build();
}
For your convenience, some properties has been added or changed in some classes. The #Builder annotation has been added to the ChallengeDto class. The rest of the corresponding entity and other classes:
a) ChallengeLike.java
#Entity
#Data
public class ChallengeLike {
#Id
#GeneratedValue
#Column(name = "challenge_like_id")
private Long id;
#ManyToOne
#JoinColumn(name = "user_id")
#JsonIgnoreProperties("challengeLikes")
private User user;
#ManyToOne
#JoinColumn(name = "challenge_id")
#JsonIgnoreProperties("challengeLikes")
private Challenge challenge;
private boolean likeDone;
private LocalDateTime createDate;
}
b) Challenge.java
#Entity
#Data
public class Challenge {
#Id
private Long id;
private Category category;
private String title;
private String subTitle;
private int totalScore;
private int requiredScore;
#OneToMany(mappedBy = "challenge", cascade = CascadeType.ALL)
#JsonIgnoreProperties("challenge")
private List<ChallengeLike> challengeLikes = new ArrayList<>();
}
c) Category.java
public enum Category {
CAT_A,
CAT_B
}
Update
If you want to fetch Challenge entity instead of ChallengeLike and map that to ChallengeDto, first implement ChallangeRepository:
#Repository
public interface ChallengeRepository extends JpaRepository<Challenge, Long> {
}
Add the fetchType to EAGER in Challange Entity class:
#OneToMany(mappedBy = "challenge", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JsonIgnoreProperties("challenge")
private List<ChallengeLike> challengeLikes = new ArrayList<>();
And to map the Challenge to ChallengeDto, you can add another mothod as follows:
private ChallengeDto mapToDto(Challenge x) {
return ChallengeDto.builder()
.category(x.getCategory())
.id(x.getId())
.like(!x.getChallengeLikes().isEmpty() && x.getChallengeLikes().get(0).isLikeDone())
.requiredScore(x.getRequiredScore())
.subTitle(x.getSubTitle())
.title(x.getTitle())
.totalScore(x.getTotalScore())
.userId(x.getUserId()) // if you have user reference in Challenge, remove this otherwise.
.build();
}
finally, to incorporate everything properly, change the caller:
public List<ChallengeDto> findChallenges(Long userId) {
List<Challenge> entities = this.repository.findAll();
List<ChallengeDto> entitiesWithoutChallengeLikes = entities.stream()
.filter(x -> x.getChallengeLikes() == null
|| x.getChallengeLikes().isEmpty())
.map(this::mapToDto).collect(Collectors.toList());
List<ChallengeDto> entitiesInferredFromChallengeLikes = entities.stream()
.filter(x -> x.getChallengeLikes() != null && !x.getChallengeLikes().isEmpty())
.flatMap(x -> x.getChallengeLikes().stream())
.map(this::mapToDto)
.collect(Collectors.toList());
entitiesInferredFromChallengeLikes.addAll(entitiesWithoutChallengeLikes);
return entitiesInferredFromChallengeLikes;
}
Final Update
Well, I finally understood properly what you expected. Adopt the following changes to the previous solution and you will get exactly what you want.
Change the 2 occurrence of the following in the findChallanges method:
.map(this::mapToDto)
To:
.map(x -> mapToDto(x, userId))
And the two mapToDto functions will be changed to follows:
private ChallengeDto mapToDto(ChallengeLike x, long userId) {
return ChallengeDto.builder()
.category(x.getChallenge().getCategory())
.id(x.getChallenge().getId())
.like(x.getUser().getId() == userId && x.isLikeDone())
.requiredScore(x.getChallenge().getRequiredScore())
.subTitle(x.getChallenge().getSubTitle())
.title(x.getChallenge().getTitle())
.totalScore(x.getChallenge().getTotalScore())
.userId(x.getUser().getId())
.build();
}
private ChallengeDto mapToDto(Challenge x, long userId) {
return ChallengeDto.builder()
.category(x.getCategory())
.id(x.getId())
.like(false)
.requiredScore(x.getRequiredScore())
.subTitle(x.getSubTitle())
.title(x.getTitle())
.totalScore(x.getTotalScore())
.userId(userId)
.build();
}
I have two tables with one to many relation. Lead and Leadactivity. A lead can have multiple activities.
Problem Statement -
I want an additional column in lead table to know the last modified date of any lead. Last modified date will be the date when last activity was created or updated. So, I am using #Formula to fetch the column. However, I am not able to get the correct date instead I am getting null value for field lastModifiedDate. Can anyone help where I am going wrong. Below are the table structure
#Entity
#Table(name = "customer_lead")
#Where(clause = ReusableFields.SOFT_DELETED_CLAUSE)
#Audited(withModifiedFlag = true)
#Data
public class Lead extends ReusableFields implements Serializable
{
//other fields
#NotAudited
#Formula("(Select max(modified) from lead_activity la Where la.lead_id=lead_id)")
Date lastModifiedDate;
}
Lead Activity
#Entity
#Table(name = "LeadActivity")
#Data
#Where(clause = ReusableFields.SOFT_DELETED_CLAUSE)
public class LeadActivity extends ReusableFields implements Serializable
{
// other fields
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "lead_id", nullable = false)
#JsonIgnoreProperties(
{ "hibernateLazyInitializer", "handler" })
#NotFound(action = NotFoundAction.IGNORE)
Lead lead;
}
Mapped super class for modified field
#MappedSuperclass
#Audited
public class ReusableFields implements Serializable
{
public static final String SOFT_DELETED_CLAUSE = "is_deleted = 'false'";
#Column(name="is_deleted", columnDefinition="BOOLEAN DEFAULT true")
public boolean isDeleted;
#CreationTimestamp
#Column(name = "created_at")
#JsonProperty("created")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss ")
private Date created;
#Column(name = "updated_at")
#JsonProperty("updated")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
#UpdateTimestamp
private Date modified;
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Date getModified() {
return modified;
}
public void setModified(Date modified) {
this.modified = modified;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean isDeleted) {
this.isDeleted = isDeleted;
}
public static String getSoftDeletedClause() {
return SOFT_DELETED_CLAUSE;
}
}
Handled this by adding below formula
#NotAudited
#Formula("(Select max(la.updated_at) from Lead_Activity la Where la.lead_id=lead_id)")
Date lastModifiedDate;
I've got a problem, I made a CRUD in springboot with MYSQL and now I want to create a method which will return update history of my object...
I have class like:
#Entity
#Table
#EntityListeners(AuditingEntityListener.class)
#JsonIgnoreProperties(value = {"createdAt", "updatedAt"}, allowGetters = true)
#Audited
public class Note implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Getter
#Setter
private Long id;
#NotBlank
#Getter
#Setter
private String title;
#Version
#Getter
#Setter
private long version;
#NotBlank
#Getter
#Setter
private String content;
#Column(nullable = false, updatable = false)
#Temporal(TemporalType.TIMESTAMP)
#CreatedDate
#Getter
#Setter
private Date createdAt;
#Column(nullable = false)
#Temporal(TemporalType.TIMESTAMP)
#LastModifiedDate
#Getter
#Setter
private Date updatedAt;
}
But I don't know how can I now create a HTTP call to show that history of updates by #Audited.
I found something like this: Find max revision of each entity less than or equal to given revision with envers
But I don't know how to implement it in my project...
#RestController
#RequestMapping("/api")
public class NoteController
{
#Autowired
NoteRevisionService noteRevisionService;
#Autowired
NoteRepository noteRepository;
// Get All Notes
#GetMapping("/notes")
public List<Note> getAllNotes() {
return noteRepository.findAll();
}
// Create a new Note
#PostMapping("/notes")
public Note createNote(#Valid #RequestBody Note note) {
return noteRepository.save(note);
}
// Get a Single Note
#GetMapping("/notes/{id}")
public Note getNoteById(#PathVariable(value = "id") Long noteId) {
return noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
}
#GetMapping("/notes/{id}/version")
public List<?> getVersions(#PathVariable(value = "id") Long noteId)
{
return noteRevisionService.getNoteUpdates(noteId);
}
// Update a Note
#PutMapping("/notes/{id}")
public Note updateNote(#PathVariable(value = "id") Long noteId,
#Valid #RequestBody Note noteDetails) {
Note note = noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
note.setTitle(noteDetails.getTitle());
note.setContent(noteDetails.getContent());
Note updatedNote = noteRepository.save(note);
return updatedNote;
}
// Delete a Note
#DeleteMapping("/notes/{id}")
public ResponseEntity<?> deleteNote(#PathVariable(value = "id") Long noteId) {
Note note = noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
noteRepository.delete(note);
return ResponseEntity.ok().build();
}
}
getVersions its the call of function which Joe Doe sent me.
There: Repository
#Repository
public interface NoteRepository extends JpaRepository<Note, Long>
{
}
You can use AuditQuery for this. The getNoteUpdates method below returns a list of mappings. Each mapping contains an object state and the time of the update that led to that state.
#Service
#Transactional
public class NoteRevisionService {
private static final Logger logger = LoggerFactory.getLogger(NoteRevisionService.class);
#PersistenceContext
private EntityManager entityManager;
#SuppressWarnings("unchecked")
public List<Map.Entry<Note, Date>> getNoteUpdates(Long noteId) {
AuditReader auditReader = AuditReaderFactory.get(entityManager);
AuditQuery query = auditReader.createQuery()
.forRevisionsOfEntity(Note.class, false, false)
.add(AuditEntity.id().eq(noteId)) // if you remove this line, you'll get an update history of all Notes
.add(AuditEntity.revisionType().eq(RevisionType.MOD)); // we're only interested in MODifications
List<Object[]> revisions = (List<Object[]>) query.getResultList();
List<Map.Entry<Note, Date>> results = new ArrayList<>();
for (Object[] result : revisions) {
Note note = (Note) result[0];
DefaultRevisionEntity revisionEntity = (DefaultRevisionEntity) result[1];
logger.info("The content of the note updated at {} was {}", revisionEntity.getRevisionDate(), note.getContent());
results.add(new SimpleEntry<>(note, revisionEntity.getRevisionDate()));
}
return results;
}
}
Note that if you can restrict the query somehow (for example by filtering on a property), you should definitely do it, because otherwise performing the query can have a negative impact on the performance of your entire application (the size of the returned list might be huge if this object was often updated).
Since the class has been annotated with the #Service annotation, you can inject/autowire NoteRevisionService like any other regular Spring bean, particularly in a controller that handles a GET request and delegates to that service.
UPDATE
I didn't know that extra steps had to be taken to serialize a list of map entries. There may be a better solution but the following approach gets the job done and you can customize the format of the output revisionDate with a simple annotation.
You need to define another class, say NoteUpdatePair, like so:
public class NoteUpdatePair {
private Note note;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private Date revisionDate; // this field is of type java.util.Date (not java.sql.Date)
NoteUpdatePair() {}
public NoteUpdatePair(Note note, Date revisionDate) {
this.note = note;
this.revisionDate = revisionDate;
}
public Note getNote() {
return note;
}
public void setNote(Note note) {
this.note = note;
}
public Date getRevisionDate() {
return revisionDate;
}
public void setRevisionDate(Date revisionDate) {
this.revisionDate = revisionDate;
}
}
and now, instead of returning a list of map entries, you'll return a list of NodeUpdatePair objects:
#Service
#Transactional
public class NoteRevisionService {
private static final Logger logger = LoggerFactory.getLogger(NoteRevisionService.class);
#PersistenceContext
private EntityManager entityManager;
#SuppressWarnings("unchecked")
public List<NoteUpdatePair> getNoteUpdates(Long noteId) {
AuditReader auditReader = AuditReaderFactory.get(entityManager);
AuditQuery query = auditReader.createQuery()
.forRevisionsOfEntity(Note.class, false, false)
.add(AuditEntity.id().eq(noteId)) // if you remove this line, you'll get an update history of all Notes
.add(AuditEntity.revisionType().eq(RevisionType.MOD)); // we're only interested in MODifications
List<Object[]> revisions = (List<Object[]>) query.getResultList();
List<NoteUpdatePair> results = new ArrayList<>();
for (Object[] result : revisions) {
Note note = (Note) result[0];
DefaultRevisionEntity revisionEntity = (DefaultRevisionEntity) result[1];
logger.info("The content was {}, updated at {}", note.getContent(), revisionEntity.getRevisionDate());
results.add(new NoteUpdatePair(note, revisionEntity.getRevisionDate()));
}
return results;
}
}
Regarding your question about the service's usage, I can see that you've already autowired it into your controller, so all you need to do is expose an appropriate method in your NoteController:
#RestController
#RequestMapping("/api")
public class NoteController {
#Autowired
private NoteRevisionService revisionService;
/*
the rest of your code...
*/
#GetMapping("/notes/{noteId}/updates")
public List<NoteUpdatePair> getNoteUpdates(#PathVariable Long noteId) {
return revisionService.getNoteUpdates(noteId);
}
}
Now when you send a GET request to ~/api/notes/1/updates (assuming nodeId is valid), the output should be properly serialized.
I have a small example with some get/post mappings and JpaRepository calls in Spring Boot.
Firstly I have two entity Classes:
#Entity
#Table(name = "stock")
public class Stock extends BaseEntity
{
#Column(name = "value")
public String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
#Entity
#Table(name = "stock_item")
public class StockItem extends BaseEntity
{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "stock_id", insertable = false, updatable = false)
public Stock stock;
#Column(name = "stock_id")
public Long stockId;
#Column(name = "value")
public String value;
}
I have a many-to-one association from StockItem to Stock.
I insert a Stock and have a controller as below:
#Autowired
public Controller(StockItemRepository stockItemRepository) {
this.stockItemRepository = stockItemRepository;
}
#RequestMapping("/")
#Transactional(readOnly = true)
public String get() {
List<StockItem> stockItemList = stockItemRepository.getItemsById(1L);
System.out.println("TX MANAGER: " + TransactionSynchronizationManager.isActualTransactionActive());
for (StockItem stockItem : stockItemList) {
System.out.println(stockItem.getStock().getValue());
}
return "get";
}
#RequestMapping("/fromSave")
#Transactional
public String post() {
StockItem stockItem = new StockItem();
stockItem.setStockId(1L);
stockItemRepository.saveAndFlush(stockItem);
System.out.println("saveCalled");
return get();
}
and getItemsById in the repository is defined as follows:
#Query("FROM StockItem si " +
"JOIN FETCH si.stock stk " +
"WHERE si.stockId = :id")
List<StockItem> getItemsById(#Param("id") Long id);
From my understanding, when I call the post method:
it creates a new item
sets the id of the associated attribute
saves and ends the transaction
Heres where things get strange...
I call get after the post and make the above repository call, which has a join fetch and when I call stockitem.getStock().getValue() I get a null pointer when I expect a LazyInitializationException.
If I call the get() from the mapping, outside the class, it successfully loads the associated object.
I have even removed the #Transaction annotation from the get, as well as
the join-fetch from my query and again, if I call from outside of the class it works and from the post, it crashes with a NullPointerException.
I have put the get inside of a TransactionTemplate.execute() and I still get a NullPointerException when calling from inside the class.
So the main questions are:
Why am I getting a NullPointerException instead of LazyInitializationException?
What is the transaction magic behind having no transaction but successfully fetching a lazy attribute??
The problem here is that you are misusing JPA. As you are seemingly aware judging from the comments on the other answer you have mapped the stock_id column twice. Once as a many-to-one relationship
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "stock_id", insertable = false, updatable = false)
public Stock stock;
and once as a simple column
#Column(name = "stock_id")
public Long stockId;
When you set the simple column and flush the changes as in your post() method the following happens:
the value gets set in the simple column. The reference is still null.
the value gets stored in the database. The reference is still null.
The repository call will find the id of the StockItemin the Persistence Context and return that instance, i.e. the exact same used in the post method, with the reference still null.
What is the transaction magic behind having no transaction but successfully fetching a lazy attribute??
No magic involved here. fetch specifications are only used for object traversal. JPQL queries don't honor these.
The unasked question remains: how to fix the situation?
The obvious fix is to lose the simple column and just use entity references as intended by JPA.
You don't want to do that in order to avoid DB access somewhere. But as long as you only access the id of the referenced Stock it shouldn't get initialized. So it seems that this should be possible with just Lazy Fetching.
Alternatively, I'd suggest removing the many-to-one relationship and creating a repository for Stock and manually loading it when required.
#Entity
#Table(name = "stock_item")
public class StockItem extends BaseEntity
{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "stock_id", insertable = false, updatable = false) //here is your problem
public Stock stock;
#Column(name = "stock_id")
public Long stockId; // why explicitly define a separate column for foreign key after mapping it above
#Column(name = "value")
public String value;
}
with insertable = false and updatable = false it won't insert in your DB and neither it will allow updation, so you are getting NullPointerException. You should atleast allow insertion in order to run the query based on the foreign key stock_id
UPDATE
Change your Entity class with property-based access:
#Entity
#Table(name = "stock_item")
public class StockItem extends BaseEntity
{
private Stock stock; // variables should always be private since you have getters and setters
private String value;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "stock_id", updatable = false)
public Stock getStock() {
return stock;
}
public void setStock(Stock stock) {
this.stock = stock;
}
#Basic
#Column(name = "value")
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
I would like to achieve the following. I have a query and I would like to run it and return rows in a REST call.
I do not want to map the query to a physical table, how would I achieve this?
I use Spring Boot 1.5.2.
After some try and fixes, I got the following solution.
Create a POJO class, no #Entity annotation. You want to add packageScan instructions if it is not found.
public class ActivityReport1 {
#Column
private BigInteger id;
#Column
private String title;
//Only getters
public ActivityReport1(BigInteger id,
String title){
this.id = id;
this.title = title;
}
In a class which is annotated with #Entity create the resultset mapping
#SqlResultSetMappings({
#SqlResultSetMapping(name = "ActivityReport1Mapping",
classes = {
#ConstructorResult(targetClass = ActivityReport1.class, columns = {
#ColumnResult(name = "id"),
#ColumnResult(name = "title")
})
})
})
Add repository class
#Repository
#Transactional
public class IActivityReport1Repository {
#PersistenceContext
private EntityManager entityManager;
public List<ActivityReport1> getResults(String userLogin) {
Query query = entityManager.createNativeQuery(
"SELECT " +
"t.request_id as id, t.request_title as title " +
"FROM some_table t ", "ActivityReport1Mapping");
List<ActivityReport1> results = query.getResultList();
return results;
}
}
And finally, the service impl class.
#Service
#Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class ActivityReport1ServiceImpl implements IActivityReport1Service {
private static final Logger _Logger = LoggerFactory.getLogger(ActivityReport1ServiceImpl.class);
#Autowired
private IActivityReport1Repository sessionFactory;
#Override
public List<ActivityReport1> runReport(String userLogin) {
List<ActivityReport1> reportRows = sessionFactory.getResults(userLogin);
return reportRows;
}
}
If you face with "Could not locate appropriate constructor", this means that on Java side it could not map db types to java types.
In my case I had to change id from Long to BigInteger and Timestamp to java.util.date.