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

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.

Related

Spring Data JPA #OneToOne mapping is not projected

This question is already phrased as an issue here: https://github.com/spring-projects/spring-data-jpa/issues/2369 but for lack of a reaction there I am copying the contents of that issue here, hoping that somebody might find what's wrong with my code or confirm that this could be a bug:
I've set up an example project here that showcases what seems to be a bug in Spring Data projections: https://github.com/joheb-mohemian/gs-accessing-data-jpa/tree/primary-key-join-column-projection-bug/complete
I have a Customer entity that has a OneToOne mapping to an Address entity:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
#OneToOne(mappedBy = "customer", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Address address;
//...
}
#Entity
public class Address {
#Id
#Column(name = "customer_id")
private Long id;
#OneToOne
#MapsId
#JoinColumn(name = "customer_id")
private Customer customer;
private String street;
//...
}
Then there are simple projection interfaces:
public interface CustomerProjection {
String getFirstName();
String getLastName();
AddressProjection getAddress();
}
public interface AddressProjection {
String getStreet();
}
But when I try to fetch a projected entity from a repository method like this one:
public interface CustomerRepository extends CrudRepository<Customer, Long> {
//...
<T> T findById(long id, Class<T> type);
}
, getAddress() on the projection will be null, whereas getAddress() when fetching the entity type is populated correctly. Of these two unit tests, only testEntityWithOneToOne()will be successful:
#BeforeEach
void setUpData() {
customer = new Customer("first", "last");
Address address = new Address(customer, "street");
customer.setAddress(address);
entityManager.persist(address);
entityManager.persist(customer);
}
#Test
void testEntityWithOneToOne() {
Customer customerEntity = customers.findById(customer.getId().longValue());
assertThat(customerEntity.getAddress()).isNotNull();
}
#Test
void testProjectionWithOneToOne() {
CustomerProjection customerProjection = customers.findById(customer.getId(), CustomerProjection.class);
assertThat(customerProjection.getAddress()).isNotNull();
}
What's the problem here?

Will JPA generate more than one auto generated coulmns

everyone I have a requirement that I want to generate two auto-generated values for two different columns. I am using Azure SQL DB as my RDBMS.
and I am using spring data JPA to persist my values.
Example:
#Entity
#Table(name="T_JUST_FOR_TEST")
public class TJustForTest implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="SEQ_GEN", sequenceName="SEQ_JUST_FOR_TEST", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_GEN")
private long id;
private String userRegistrationId;
public TJustForTest() {}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
}
here, I would like to use Id as my primary key as auto-generated and userRegistration Id also auto-generated but not primary key just a unique key and some custom format
Example
I will append some string as prefix and id as a suffix, meaning I will use the same primary key to generate the userRegistration No.
is there any way to achieve this or any other way around it, please clarify.
finally, I found a better solution to generate userRegistration by using Id,
I need to write one Listener class to get the auto-generated Id in my using #PostPersist
annotation, actually this will be called once the entity object persists in DB.
public class TJustForTestListener {
#PostPersist
public void getPostPersist(TJustForTest ob) {
try {
ob.setuserRegistrationId("CR"+ob.getId());
}catch(Exception e) {
e.printStackTrace();
}
}
and in Entity level i need to declare my listener class by using #EntityListeners
#Entity
#Table(name="T_JUST_FOR_TEST")
#EntityListeners(TJustForTestListener .class)
public class TJustForTest implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="SEQ_GEN", sequenceName="SEQ_JUST_FOR_TEST", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_GEN")
private long id;
private String userRegistrationId;
public TJustForTest() {}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
}
that's all it is required, JPA will insert the record first then update automatically.

There is no ID defined for this entity hierarchy

I am stuck with this error message, that appears every time I want to add a ManytoOne relationship with another entity class.
The class must use a consistent access type (either field or property). There is no ID defined for this entity hierarchy
This is my entity Transaction
#Entity
#Table(name = "CustomerTransaction")
public class CustomerTransaction implements Serializable {//this is the line with the error message
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne //This generates the problem
#JoinColumns({
#JoinColumn(name = "CUS_ID", referencedColumnName = "IDCUSTOMER") })
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
private long transactionID;
#Temporal(TemporalType.TIMESTAMP)
private Date buyDate;
public Date getBuyDate() {
return buyDate;
}
public void setBuyDate(Date buyDate) {
this.buyDate = buyDate;
}
public long getTransactionID() {
return transactionID;
}
public void setTransactionID(long transactionID) {
this.transactionID = transactionID;
}
public String getCarYear() {
return carYear;
}
public void setCarYear(String carYear) {
this.carYear = carYear;
}
public Date getTransactionDate() {
return transactionDate;
}
public void setTransactionDate(Date transactionDate) {
this.transactionDate = transactionDate;
}
private String carYear;
#Temporal(TemporalType.TIMESTAMP)
private Date transactionDate;
JPA annotation should all be placed either on fields or on accessor methods. You've placed the #Id and #GeneratedValue annotation on a field (private Long id), but #ManyToOne and #JoinColumns on a getter (public Long getId()). Move the latter on a field as well.
i had similar error but in the end, i realized #Id was referencing this package org.springframework.data.annotation.Id instead of javax.persistence.Id. i was using #MappedSuperClass approach so as soon as i corrected this, everything worked fine
You need to import #Id from "import javax.persistence.Id;"

Return more data than model contains using 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.

Loading child entities in parent/child relationship with JPA

My domain has a Category entity which has a biderectional relationship on itself. Each category can have a parent and children.
#Entity
public class Category implements DomainObject {
private Long id;
private Integer version;
private String name;
private Category parent;
private Set<Category> children;
#Override
#Id
#GeneratedValue
public final Long getId() {
return id;
}
#Version
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public void setId(Long id) {
this.id = id;
}
#Column(unique=true, nullable=false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#ManyToOne
public Category getParent() {
return parent;
}
public void setParent(Category parent) {
this.parent = parent;
}
#OneToMany
#JoinColumn(name = "parent_id")
public Set<Category> getChildren() {
return children;
}
public void setChildren(Set<Category> children) {
this.children = children;
}
}
I have created the following query to fetch the "root" categories with their direct (level 1) children.
select distinct c from Category c left join fetch c.children where c.parent is null order by c.name
This actually works. My question is: why do I need the "JoinColumn" annotation on getChildren() to make this work and why can't I just make a "foin fetch" query, without "distinct"? If I remove "distinct" I get a multiplication. For each child of a parent, the entire parent is copied in the result set.
Is there a better way to do this? It just feels... a bit crappy.
In JPA you need to set distinct when you join a OneToMany, otherwise it will return duplicates.
This is required.
The JPA spec requires this, but it is an odd default, but relates to what happens in database joins.