I have a simple JPA entity with #AdditionalCriteria mentioned for the login language. I also have specified a query redirector for this class. When I attempt to get the translated sql string in the query redirector, I get a null pointer exception. The reason is that the field in the entity is called lang and the additional criteria parameter is LOGIN_LANGUAGE. The exception is thrown when the line 273 of class org.eclipse.persistence.internal.expressions.ParameterExpression is executed.
My JPA entity looks like this
#QueryRedirectors(allQueries=VPMQueryRedirector.class)
#AdditionalCriteria(value = "this.lang = :LOGIN_LANGUAGE")
public class AuthorityTextView extends EntityCommons implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "AUTHORITYID", length = 36)
private String authorityId;
#Id
#Column(name = "LANG", length = 2)
private String lang;
#Column(name = "AUTHORITYTEXT", length = 255)
private String authorityText;
#Column(name = "DEFAULTUSED")
private Boolean defaultUsed;
public String getAuthorityId() {
return authorityId;
}
public String getLang() {
return lang;
}
public String getAuthorityText() {
return this.authorityText;
}
public Boolean getDefaultUsed() {
return this.defaultUsed;
}
}
My Query Redirector is listed below
public class VPMQueryRedirector implements QueryRedirector {
private static final long serialVersionUID = 3912645701055442481L;
private Logger logger = LoggerFactory.getLogger(getClass());
#Override
public Object invokeQuery(DatabaseQuery query, Record arguments, Session session) {
query.setDoNotRedirect(true);
String translatedSQLString = query.getTranslatedSQLString(session, arguments);
}
I have create a bug under eclipselink, but there hasn't been any updates yet if the observation is correct or not.
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.
Scenario
I have 1 Collection: Discussion
Discussion Collection contains 2 embedded Documents(one inside another)
Discussion contains reply, reply contains comment
I am using MongoTemplate in my Project (Spring with MongoDB)
My POJO Details:
Discussion
#Document(collection = "discussion")
public class Discussion {
private String discussion_id;
private String title;
private String description;
private Date discussion_time;
private String initiated_by;
private Set<String> topic_tags;
private int replies_count;
// List of Replies
private List<Reply> replies;
}
Reply
public class Reply {
private String reply_id;
private String replied_by;
private String reply_content;
private Date replied_date;
private float rating;
//List of Comments
private List<Comment> comments;
}
Comment
public class Comment {
private String comment_id;
private String commented_by;
private String comment_content;
private Date commented_date;
}
Hint
I can able to delete reply from Discussion Collection.Code as follows
public void deleteReply(String reply_id, String replied_by, String initiated_by) {
if (replied_by.equals(initiated_by)) {
Query query = new Query(Criteria.where("initiated_by").is(initiated_by));
query.fields().elemMatch("replies", Criteria.where("reply_id").is(reply_id));
Update update = new Update();
update.pull("replies", new BasicDBObject("reply_id", reply_id));
mongoTemplate.updateMulti(query, update, COLLECTION_NAME);
logger.info("deleted reply successfully ");
} else {
logger.info(" Unauthoriszed to delete ");
}
}
I want to delete a comment from Discussion Collection. can any one provide the solution.
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.
I have asked a similar question these days, and after many many hours of trials and fails i find myself compelled to describe my problem from a different point of view.
So I have two entities, WaiterEntity and OrderEntity - as logic demands it, a waiter can have many orders, but an order just one waiter. When the method that persists orders is called, the given order is persisted correctly into the database. But when the waiter is asked about his orders with getOrders(), an empty list is returned. I tried to solve this like many tutorials tell, by (right after persisting the order) getting the list of orders from the waiter and adding the order to the list. Unfortunately there is very strange behaviour to observe: adding the line waiter.getOrders().add(order) somehow prevents or reverts the order to be persisted into the database. But when I try to get the waiter's orders, the all orders that previously were tried to persist appear correctly in the database, but at once the tables of WaiterEntity and OrderEntity become unreadable for JPA. (Although, I can still see the correct table contents through manually called SQL queries.) The only thing that helps is rebuilding the tables.
So maybe some properties of persistence.xml are wrong? The entity annotations are not correctly set up? Or the java code is invalid as I can't tell because I don't have much experience with JPA and GlassFish?
Here are the Entities:
#Entity
#XmlRootElement
#Table(name="WAITERENTITY")
public class WaiterEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval=true, fetch = FetchType.EAGER)
#JoinColumn(name = "waiter_id")
private List<OrderEntity> orders = new ArrayList<>();
... getters and setters
}
#Entity
#XmlRootElement
#Table(name="ORDERENTITY")
public class OrderEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long orderNumber;
#ManyToOne (cascade = CascadeType.ALL)
#JoinColumn (name = "table_id")
private TableEntity table_;
private int sumOfMoney = 0;
private boolean finalized = false;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval=true, fetch = FetchType.LAZY)
#JoinColumn(name = "order_id")
private List<OrderItemEntity> orderItems = new ArrayList<>();
#ManyToOne (cascade = CascadeType.DETACH)
#JoinColumn (name = "waiter_id")
private WaiterEntity waiter;
... getters and setters
}
The method to create orders:
public void create(OrderEntity e) {
WaiterEntity waiter = null;
if (e.getWaiter() != null) {
waiter = em.find(WaiterEntity.class, e.getWaiter().getId());
if (waiter != null) e.setWaiter(waiter);
}
if (e.getTable() != null) {
TableEntity table = em.find(TableEntity.class, e.getTable().getId());
if (table != null) e.setTable(table);
}
em.persist(e);
if (waiter != null) {
waiter = em.find(WaiterEntity.class, e.getWaiter().getId());
//waiter.getOrders().add(e);
}
}
As mentioned, the commented line only makes problems. Further on, without this line, everything in the database is as it should be, as in the foreign keys are set up right, but an obtained WaiterEntity has an empty list, regardless that in fact it has orders in its relationship in the database.
edit: The method that gets a waiter's orders:
public List<OrderEntity> findOrdersByWaiter(#QueryParam("id") long id) {
WaiterEntity waiter = em.find(WaiterEntity.class, id);
return waiter.getOrders();
}
As mentioned, in the situation when the commented line above is un-commented, the strage behaviour starts when calling findOrdersByWaiter(waiter.getId()).
Furthermore, no exceptions are thrown by GlassFish. It's like it just does nothing anymore when calling persitence methods that work with the tables of WaiterEntity and/or OrderEntity.
It would really help if someone more experienced told me what I am doing wrong. If further explanation or code snippets are needed for a better understanding of the situation, I will paste it here. Thanks in advance!
edit 2: (#DonovanMuller) First, a little explanation why there are different object types: I use web resources. The client program and server program communicate using JSON. Only the server knows entity models and persists them. The client does the following (I am not posting all it's methods, just the relevant ones):
IWaiterWebAppClient client = new IWaiterWebAppClient();
client.create(new WaiterBean("John Marston"));
client.create(new WaiterBean("Abigail Marston"));
WaiterBean waiter = client.findByNameSingle(WaiterBean.class, "John Marston");
int rnd = (int) Math.round(Math.random() * 100);
client.create(new TableBean(rnd));
TableBean table = client.findByNameSingle(TableBean.class, String.valueOf(rnd));
OrderBean order = new OrderBean(waiter);
order.setWaiter(null);
client.create(order);
client.create(new OrderBean(waiter, table));
System.out.println(waiter.getName() + "'s OrderBeans:\n" + client.findOrdersByWaiter(waiter.getId()));
while client is an instance of:
public class IWaiterWebAppClient {
private final WebTarget webTarget;
private final Client client;
private static final String BASE_URI = "http://localhost:8080/iWaiter_WebAppServer/webresources";
public IWaiterWebAppClient() {
client = javax.ws.rs.client.ClientBuilder.newClient();
webTarget = client.target(BASE_URI).path("iwaiter");
}
public void close() {
client.close();
}
public <T> void create(T bean) {
webTarget.path(getResourcePath(bean.getClass()))
.request()
.post(Entity.entity(bean,MediaType.APPLICATION_JSON + ";charset=UTF-8"));
}
public <T> T findByNameSingle(Class<T> type, String name) {
List<T> list = findByName(type, name);
return (!list.isEmpty() ? list.get(0) : null);
}
public <T> List<T> findByName(Class<T> type, String name) {
return webTarget.path(getResourcePath(type) + "/findbyname/{name}")
.queryParam("name", name).request(MediaType.APPLICATION_JSON)
.get(constructGenericTypeArrayList(type));
}
public List<OrderBean> findOrdersByWaiter(long id) {
List<OrderBean> list = webTarget.path("order/findbywaiter/{id}")
.queryParam("id", id).request(MediaType.APPLICATION_JSON)
.get(new GenericType<ArrayList<OrderBean>>() {});
return list;
}
private String getResourcePath(Class c) {
if (c.equals(EmployeeView.class)) return "employee";
if (c.equals(WaiterBean.class)) return "waiter";
if (c.equals(TableBean.class)) return "table";
if (c.equals(ItemBean.class)) return "availableitem";
if (c.equals(OrderBean.class)) return "order";
return "";
}
...
}
The fields, getters and setters of WaiterBean and WaiterEntity, as well as OrderBean and OrderEntity, are the same. The only difference is that the 'beans' don't have JPA annotations.
edit 3: (#DonovanMuller) The server is a resource class which methods represent HTTP methods (GET, POST, PUT, DELETE) and exchange information with JSON. The methods of the server have resource annotations, such as:
#GET #Path("order/findbywaiter/{id}")
#Produces("application/json")
public List<OrderEntity> findOrdersByWaiter(#QueryParam("id") long id) { ... }
edit 4: (#DonovanMuller) This is the main part of the web resource class that is responsible for persistence:
#Path("iwaiter")
#Stateless
public class IWaiterResource {
#PersistenceContext(unitName = "ZZZ_WebServicePU")
private EntityManager em;
#Context
private UriInfo context;
#POST #Path(PATH_WAITER)
#Consumes({"application/json", "application/xml"})
public void create(WaiterEntity e) { em.persist(e); }
#POST #Path(PATH_ORDER)
#Consumes({"application/json", "application/xml"})
public void create(OrderEntity e) {
WaiterEntity waiter = e.getWaiter();
em.persist(e);
if (waiter != null) {
waiter.getOrders().add(e);
}
}
#PUT #Path(PATH_WAITER)
#Consumes({"application/json", "application/xml"})
public void update(WaiterEntity e) { em.merge(e); }
#PUT #Path(PATH_ORDER)
#Consumes({"application/json", "application/xml"})
public void update(OrderEntity e) { em.merge(e); }
#DELETE #Path(PATH_WAITER)
#Consumes({"application/json", "application/xml"})
public void deleteWaiter(#QueryParam("id") long id) { em.remove(em.find(WaiterEntity.class, id)); }
#DELETE #Path(PATH_ORDER)
#Consumes({"application/json", "application/xml"})
public void deleteOrder(#QueryParam("id") long id) { em.remove(em.find(OrderEntity.class, id)); }
private <T> List<T> findAll(Class<T> type) {
CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
cq.select(cq.from(type));
return em.createQuery(cq).getResultList();
}
#GET #Path(PATH_WAITER)
#Produces({"application/json", "application/xml"})
public List<WaiterEntity> findAllWaiters() { return findAll(WaiterEntity.class); }
#GET #Path(PATH_ORDER)
#Produces({"application/json", "application/xml"})
public List<OrderEntity> findAllOrders() { return findAll(OrderEntity.class); }
#GET #Path(PATH_WAITER + "/" + PATH_FIND_BY_ID + "/{id}")
#Produces("application/json")
public WaiterEntity findWaiter(#PathParam("id") long id) { return em.find(WaiterEntity.class, id); }
#GET #Path(PATH_ORDER + "/" + PATH_FIND_BY_ID + "/{id}")
#Produces("application/json")
public OrderEntity findOrder(#PathParam("id") long id) { return em.find(OrderEntity.class, id); }
private <T> List<T> findByName(Class<T> type, String column, String searchTag) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T> cq = cb.createQuery(type);
Root<T> root = cq.from(type);
cq.where(cb.equal(root.get(column), searchTag));
return em.createQuery(cq).getResultList();
}
#GET #Path(PATH_WAITER + "/" + PATH_FIND_BY_NAME + "/{name}")
#Produces("application/json")
public List<WaiterEntity> findWaitersByName(#QueryParam("name") String name) { return findByName(WaiterEntity.class, "name", name); }
#GET #Path(PATH_ORDER + "/" + PATH_FIND_BY_NAME + "/{name}")
#Produces("application/json")
public List<OrderEntity> findOrdersByName(#QueryParam("name") String name) { return findByName(OrderEntity.class, "orderNumber", name); }
#GET #Path("order/findbywaiter/{id}")
#Produces("application/json")
public List<OrderEntity> findOrdersByWaiter(#QueryParam("id") long id) {
WaiterEntity waiter = em.find(WaiterEntity.class, id);
return waiter.getOrders();
}
}
Your association is not mapped correctly. A bidirectional always has an owner side and an inverse side. In a OneToMany association, the inverse side must be the one side.
The inverse side does not specify how the association is mapped. It simply says: go look at the other side of the association to see how this association is mapped. This is done using the mappedBy attribute:
In Waiter:
#OneToMany(mappedBy = "waiter", cascade = CascadeType.ALL, orphanRemoval=true, fetch = FetchType.EAGER)
private List<OrderEntity> orders = new ArrayList<>();
In Order:
#ManyToOne(cascade = CascadeType.DETACH)
#JoinColumn(name = "waiter_id")
private WaiterEntity waiter;
The way you have done it, JPA considers waiter.orders and order.waiter as two independent associations, which, unfortunately, are mapped using the same join column. The two associations thus conflict with each other.
I moved this to an answer, as the comments were getting a bit long winded.
I haven't tested this but in your create(OrderEntity e) resource method (edit 4) your waiter reference is surely detached?
#POST #Path(PATH_ORDER)
#Consumes({"application/json", "application/xml"})
public void create(OrderEntity e) {
WaiterEntity waiter = e.getWaiter(); // <-- getting a reference to a detached entity. I.e. not managed by the entity manager
em.persist(e);
if (waiter != null) {
waiter.getOrders().add(e);
}
}
If you change it to the following, does it solve your problem?
#POST #Path(PATH_ORDER)
#Consumes({"application/json", "application/xml"})
public void create(OrderEntity e) {
em.persist(e);
Waiter waiter = e.getWaiter(); // <-- The reference to the persisted OrderEntity is now managed
if (waiter != null) {
waiter.getOrders().add(e);
}
}