Return more data than model contains using Spring Data - spring-data

I'm working with Spring Data which is great stuff, but sometimes I need to get more data from database than my model can handle. For example I have model like below.
#Entity
#Table(name = "email")
public class Mail implements Serializable {
#Getter
#Setter
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long id;
#Getter
#Setter
private String text;
}
An I my query will be more complex than usual. I want to get my model and in addition number of similar entities, using group by.
#Query(value = "SELECT m, COUNT(m) as countValue FROM Mail m GROUP BY m.text")
List<Mail> findAllNewsletters();
How I should handle something like that? My model does't contain countValue so I will get List<Object[]>
How to deal with that situation, keep my code clean, easiness
of using this.

Step 1: Create a container class to hold the output from your query.
class MailOccurence {
private final Mail mail;
private final Long recurrence;
public MailOccurence(final Mail mail, final Long recurrence) {
this.mail = mail;
this.recurrence = recurrence;
}
public Mail getMail() { return mail; }
public Long getRecurrence() { return recurrence; }
}
Step 2: Populate and return instances of the container class from the query.
Query(value = "SELECT new MailOccurence(m, COUNT(m)) FROM Mail m GROUP BY m.text")
List<MailGroup> findAllNewsletters();
For full details, see the JPA specification.

You can go for a DTO like following
public class MailEntry {
private Long id;
private String text;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
and inside your business logic you can take the advantage of spring template something like following
#Autowired
JdbcTemplate jdbcTemplate;
private static final String SQL = "SELECT m, COUNT(m) as countValue FROM Mail m GROUP BY m.text";
public List<MailEntry> getMailEntries() {
List<MailEntry> mailEntryList = jdbcTemplate.query(SQL, new RowMapper<MailEntry>() {
public MailEntry mapRow(ResultSet rs, int rowNum) throws SQLException {
MailEntry mailEntry = new MailEntry();
mailEntry.setId(rs.getInt(1));
mailEntry.setText(rs.getString(2));
return mailEntry;
}
});
return mailEntryList;
}
Hope this help.

Related

#Transactional in spring JPA

I have a spring boot application where I need to update a migratedCustomer db table based on userId and phoneNumber.
Since I have to use for loop in the service layer for every update, it is creating a
new transaction and performance is hampered.
how could I make sure only one transaction is created and hence to improve the performance. code is like below
#Entity
#Table(name = "MigratedCustomer")
public class MigratedCustomer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String userId;
private String phoneNumber;
#Temporal(TemporalType.TIMESTAMP)
private Date createdTimestamp;
private int batchNumber;
private String comment;
}
public class MigratedCustomerService {
#Autowired
private UserRepository userRepository;
public void updateMsisdn(List<MigratedCustomer> savedCustomers) {
for (MigratedCustomer savedCustomer : savedCustomers) {
userRepository.updateStatus(savedCustomer.getUserId(),
savedCustomer.getPhoneNumber());
}
}
}
public interface MsisdnRepository extends JpaRepository<Msisdn, Long> {
#Modifying
#Query(value = "UPDATE Msisdn SET status=INACTIVE where userId=:userId and phoneNumber=:phoneNumber",
nativeQuery = true)
void updateStatus(#Param("userId") String userId, #Param("phoneNumber") String phoneNumber);
}

POST REST request including a foreign key OnToMany Mapping

i'm new to Springboot. I'm trying to implement a simple REST api using :
-Springboot, JPA & rest along with hibernate
I have a 2 tables database, Notebook that contains 1 to many notes
I already setup the 2 tables and relationships. I also created a NotebookRepository and NoteRepository to get basic CRUD operations via the springboot rest. The Database connection and relationships are functionning
but i don't know how to add a new note (it has a notebook_id foreign key which msut NOT be NULL) and everytime i tryto post something along these lines
{
"title:"abc",
"text":"whatever",
"notebook":{
"id":2
}
}
i get this error :
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'notebook_id' cannot be null
#Entity
#Table(name="notebook")
public class NoteBook {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String name;
#OneToMany(mappedBy="notebook", cascade=CascadeType.ALL)
List<Note> notes;
public NoteBook() {
}
public NoteBook(String name) {
this.name = name;
}
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 List<Note> getNotes() {
return notes;
}
public void setNotes(List<Note> notes) {
this.notes = notes;
}
public void addNote(Note note) {
if(notes == null) {
notes = new ArrayList<>();
}
note.setNotebook(this);
notes.add(note);
}
#Entity
#Table(name="note")
public class Note {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="title")
private String title;
#Column(name="text")
private String text;
#ManyToOne(cascade={CascadeType.MERGE, CascadeType.DETACH, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name="notebook_id")
private NoteBook notebook;
public Note() {
}
public Note(String title, String text) {
this.title = title;
this.text = text;
}
#RepositoryRestResource(collectionResourceRel = "note", path = "notes")
public interface NoteRepository extends JpaRepository<Note, Integer>{
//No code...
}
#RepositoryRestResource(collectionResourceRel = "notebook", path = "notebooks")
public interface NotebookRepository extends JpaRepository<NoteBook, Integer>{
}
The problem is that the class Note doesn't have a constructor with NoteBook parameter to pass the created NoteBook object to, so the solution is to add this constructor:
public Note(String title, String text, NoteBook noteBook) {
this.title = title;
this.text = text;
this.noteBook = noteBook;
}
and it's enough to send the JSON object as you do, but just be aware of case-sensitivity:
{ "title:"abc", "text":"whatever", "noteBook":{ "id":2 } }
I think you need to add referencedColumnName = "id" for JoinColumn annotation for notebook field in Note class.
Maybe you have problem with IDENTITY generation type. See this problem with null pointer

JPA One-to-Many relationship using a List - OrderBy ignored/not working

I'll try to formulate the question more simple:
#Entity
public class One implements Serializable {
...
#Id
#GeneratedValue
private Long id;
#OneToMany
#OrderBy("name ASC")
private List<Many> many;
...
First I populate the List with some Many-Entities and persist the One-Entity. Second I retrieve (em.find) the One-Entity expecting the List in ascending order by Many#name, but it's not ordered by name. The List is ordered by id. Complete code see below if necessary.
Original post some days ago:
I'm using a current Netbeans Glassfish bundle.
Product Version: NetBeans IDE 8.0 (Build 201403101706)
Updates: NetBeans IDE is updated to version NetBeans 8.0 Patch 2
Java: 1.7.0_51; Java HotSpot(TM) 64-Bit Server VM 24.51-b03
Runtime: Java(TM) SE Runtime Environment 1.7.0_51-b13
System: Mac OS X version 10.9.3 running on x86_64; UTF-8; de_DE (nb)
The JPA #OrderBy annotation is completely ignored.
#Entity
public class One implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
#OneToMany
#OrderBy("name ASC")
private List<Many> many;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<Many> getMany() {
return many;
}
public void setMany(List<Many> many) {
this.many = many;
}
}
The many Entity
#Entity
public class Many implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
private String name;
public Many() {
}
public Many(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The service class (EJB)
#Stateless
public class Service {
#PersistenceContext(unitName = "cwPU")
private EntityManager em;
public One createOne() {
return em.merge(new One());
}
public Many createMany(String name) {
return em.merge(new Many(name));
}
public One add(Long oneId, Long manyId) {
One one = em.find(One.class, oneId);
Many many = em.find(Many.class, manyId);
one.getMany().add(many);
return one;
}
public One find(Long id) {
One one = em.find(One.class, id);
return one;
}
}
The main class
public class Main {
public static void main(String[] args) throws NamingException {
EJBContainer container = EJBContainer.createEJBContainer();
Context ctx = container.getContext();
Service service = (Service) ctx.lookup("java:global/classes/Service");
One one = service.createOne();
Many many = service.createMany("the-first");
service.add(one.getId(), many.getId());
many = service.createMany("a-second");
one = service.add(one.getId(), many.getId());
one = service.find(one.getId());
System.out.println("-------------------------------------------------");
for (Many m : one.getMany()) {
System.out.println(m.getName());
}
container.close();
}
}
The output:
the-first
a-second
No matter what I write to the #OrderBy annotation (name ASC, name DESC, id ASC, id DESC), the output is always the same ascending order by the id.
Any idea what I'm missing?
The #Orderby annotation doesn't actually work that way. According to the javadoc, the annotation "Specifies the ordering of the elements of a collection ...at the point when the collection is retrieved."
So the annotation affects the result of the query (find), but does not dictate the order in the collection you store the result set into.
The solution is calling em.refresh (at the right place) as stated from Chris and WPrecht. I had to do this in a separate EJB method.
This did not work:
public One find(Long id) {
em.refresh(em.find(One.class, id)); // did not work
One one = em.find(One.class, id);
return one;
}
Adding a separate refresh method
public void refresh(Long id) {
em.refresh(em.find(One.class, id));
}
and calling it in the main program
...
service.refresh(one.getId());
one = service.find(one.getId());
...
works!
Probably I have to do more reading to understand caching.

JPA EmbeddedId is null after persisting

Let's say I have 2 JPA entites
#Entity
public class MyComplexEntity implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
#GeneratedValue(strategy = GenerationType.IDENTITY)
private MyComplexEntityId id;
private String text;
public MyComplexEntity() {
}
public MyComplexEntity(String text) {
this.text = text;
}
public MyComplexEntityId getId() {
return id;
}
public void setId(MyComplexEntityId id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
#Override
public String toString() {
return "MyComplexEntity{" + "id=" + id + ", text=" + text + '}';
}
}
and
#Embeddable
public class MyComplexEntityId {
#Column
private long id;
public MyComplexEntityId(long id) {
this.id = id;
}
public MyComplexEntityId() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Override
public String toString() {
return "MyComplexEntityId{" + "id=" + id + '}';
}
}
Now I'm trying to persist a new MyComplexEntity object like this
MyComplexEntity entity = new MyComplexEntity("something");
em.persist(entity);
System.out.println(entity);
and what I get is this:
MyComplexEntity{id=null, text=something}
Why is the id null there? If I do the same thing with a primitive primary key type, the id is correctly set after persisting. I've also tried the following things:
Calling em.flush(); after the persist: does nothing
Calling em.refresh(entity); after the persist: Exception telling me that the entity is no longer in the database (which is wrong)
Trying em.merge() instead of em.persist(): Same result, as expected
Calling em.contains(entity); returns true, so the entity is actually attached
Putting the em.persist(); in a separate transaction by executing it in a separate method using #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
There has to be some kind of problem with persist(); when using an #EmbeddedId, but I just don't get it.
You cannot put GeneratedValue(strategy = GenerationType.IDENTITY) on a complex mapping. It needs to go within the embeddedId class, and you need to instantiate a MyComplexEntityId instance for your new Entity. You will also need to flush to the database to ensure that values are assigned as identity values are assigned by the insert.

Google Objectify v4 fetch by id

I'm trying to fetch an entity in App Engine with Objectify v4 but it doesn't work.
My #Entity: Translation.class
The #Id of the #Entity I want to fetch: 301L
My #Entity:
#Entity
public class Translation {
#Id
private Long id;
private String text;
public String getText() {
return text;
}
public Long getId() {
return id;
}
public void setText(String text) {
this.text = text;
}
}
The request that doesn't word:
Translation translation =ObjectifyService.ofy().load().type(Translation.class).id(301L).get(); // translation is null
But if I do:
Translation translation = ObjectifyService.ofy().load().type(Translation.class).first().get(); // translation is not null
Then:
System.out.println(translation.getId()); // translation id equal 301
So the fetch by id doesn't seem to work.
Where is the problem?
Since your entity has a #Parent field, in order to get it by id, you need to execute:
Translation translation = ObjectifyService.ofy().load().type(Thing.class).parent(par).id(301).get();
For more information take a look at Objectify Basic Operations - Loading
Hope this helps!
for #stickfigure, this is my real #Entity (PartOfSpeechGroup is obviously an #Entity too).
#Entity
public class Translation implements IsSerializable {
#Id
private Long id;
private String text;
#Parent
private Key<PartOfSpeechGroup> partOfSpeechEntryKey;
public Translation() {}
public Translation(Key<PartOfSpeechGroup> partOfSpeechEntryKey) {
this.partOfSpeechEntryKey = partOfSpeechEntryKey;
}
public String getText() {
return text;
}
public Long getId() {
return id;
}
public Key<PartOfSpeechGroup> getPartOfSpeechEntryKey() {
return partOfSpeechEntryKey;
}
public void setText(String text) {
this.text = text;
}
}