I have this table which I would like to store different values as keys and vales:
#Entity
#Table(name = "wpf_payment_attributes")
public class WpfPaymentAttributes implements Serializable {
private static final long serialVersionUID = -2629784870868584850L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#Column(length = 255)
private String name;
#Column(length = 255)
private String global_ley;
#Column(name = "VALUE", columnDefinition = "TEXT", length = 65535)
private String value;
....
}
WpfPaymentAttributes attibutes = new WpfPaymentAttributes();
attibutes.setName("usage");
attibutes.setValue("Test Usage");
attibutes.setGlobal_key(12333);
WpfPaymentAttributes attibutes = new WpfPaymentAttributes();
attibutes.setName("name");
attibutes.setValue("Peter");
attibutes.setGlobal_key(12333);
But how I can get all value with the same global key with one SQL query using JPA? The problem is that I don't know in advance what are the table columns and values.
I need to get this structure:
usage | name
-------------------
Test Usage | Peter
Is this possible with JPA?
This is not possible, since there are some issues that JPA won't be able to help you with:
there could be multiple WpfPaymentAttributes values with the same
global key and name (however, this could be solved by using a
database constraint);
there could be arbitrary values in the name
column, hence you'd have to make sure that they actually map to your expected result structure, there are no unknown "names" etc.
If you don't need a super-generic system, I'd advice you to write a simple mapper, that shouldn't be very complex. Just get all WpfPaymentAttributes by a specific global_key and apply the mapping. For example, here's the structure that you need:
public class Result {
private String usage;
private String name;
// ...
}
And then:
Result result = new Result();
List<WpfPaymentAttributes> attributes = entityManager.createQuery(
// query should be parameterized
"select a from WpfPaymentAttributes a where global_key = 12333"
).getResultList();
for (WpfPaymentAttributes attribute : attributes) {
String value = attribute.getValue();
switch(attribute.getName()) {
case "name":
result.setName(value);
break;
case "usage":
result.setUsage(value);
break;
default:
throw new IllegalStateException();
}
}
return result;
Related
I have an entity like so:
#Entity
#Table(name = "MyTable", schema = "test")
#Getter #Setter
public class PurgeSystemsEntity {
#Id
#Column(name = "id", nullable = false)
private int id;
#Column(name = "system_name", nullable = false, length = 255)
private String systemName;
.
.
}
How do I validate that the string obtained from DB (like when doing a .findAll()) in systemName field is one of the possible options defined in the Enum System :
public static enum System {
PROD, DEV, QA;
}
So, If a row is fetched with systemName value being 'STAGING', it should throw an exception immediately.
Is there some elegant way to do this?
Set the field type to the enum.
#Column(name = "system_name", nullable = false, length = 255)
#Enumerated(EnumType.STRING)
private System systemName;
This will cause an error if you encounter a value not defined in the enum.
You also have to set EnumType.STRING explicitly, as it defaults to EnumType.ORDINAL which would correspond to the enum ordinal value instead of the name
You can write your own method in ENUM
Something like this
public static MyEnum fromValue(String value) {
for (MyEnum b : MyEnum.values()) {
if (b.value.equals(value)) {
return b;
}
}
throw new IllegalArgumentException("Unexpected value '" + value + "'");
}
Alternatively, if your Enum is part of your class, I think , Java should automatically handle and throw IllegalArgumentException if the value cannot be mapped !
First sorry this is long but I wanted to provide all information possible.
I am working on a much larger query that will build on this hence the reason I am not taking an easier or other approaches. In addition I can't really change the way we implemented the DB and Domain Objects.
My problem is I can't get a Spring Data JPA Query to work with an Enum. The field is an Enum in the DB as well. Here is the abbreviated code.
The SQL for the 2 tables:
CREATE TABLE my_order (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
creation_date TIMESTAMP NOT NULL,
);
CREATE TYPE ORDER_STATE as ENUM ('new', 'open', 'closed');
CREATE CAST (CHARACTER VARYING AS ORDER_STATE) WITH INOUT AS IMPLICIT;
CREATE TABLE my_order_history (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
order_date TIMESTAMP NOT NULL,
order_state ORDER_STATE NOT NULL,
order_id INT REFERENCES my_order
);
Here is the corresponding Domain Objects:
#Entity
#Table(name = "my_order")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "creation_date")
private Date creationDate;
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<OrderHistory> orderHistories;
}
#Entity
#Table(name = "my_order_history")
public class OrderHistory {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "order_date ")
private Date orderDate;
#Column(name = "order_state")
#Convert(converter = OrderStateConverter.class)
private OrderState orderState
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "order_id", nullable = false)
private Order order;
}
Here is the converter:
#Component
#Converter(autoApply = true)
public class OrderStateConverter implement AttributeConverter<OrderState, String> {
#Override
public String convertToDatabaseColumn(OrderState attribute) {
return attribute.getValue;
}
#Override
public OrderState convertToEntityAttribute(String dbData) {
return OrderState.fromValue(dbData);
}
}
Here is the Enum:
public enum OrderState {
NEW("new"), OPEN("open"), CLOSED("closed");
#Getter
private String value;
private OrderState(String value) {
this.value = value;
}
public static OrderState fromValue(String value) {
for (OrderState orderState : values()) {
if (orderState.getValue().equals(value)) {
return orderState;
}
}
return null;
}
}
Here is my Spring Repo. I am putting the 3 ways I have tried and then below I will give you the exceptions I am receiving with each:
#Repository
public interface OrderRepository extends PagingAndSortingRepository<Order, Long> {
#Query("SELECT o FROM Order o JOIN o.orderHistories oh WHERE oh.orderState = :orderState")
List<Order> getOrdersByOrderState1(#Param("orderState") OrderState orderState);
#Query("SELECT o FROM Order o JOIN o.orderHistories oh WHERE oh.orderState = :orderState")
List<Order> getOrdersByOrderState2(#Param("orderState") String orderState);
#Query("SELECT o FROM Order o JOIN o.orderHistories oh WHERE oh.orderState = :#(#orderState?.getValue())")
List<Order> getOrdersByOrderState3(#Param("orderState") OrderState orderState);
}
For #1 when I provide an OrderState enum I get the following exception:
Caused by: org.postgres.util.PSQLException: ERROR: operator does not exist: order_state = character varying
Hint: No operator matches the given name and argument types. You might need explicit type casts.
For #2 when I provide OrderState.getValue(), which is a String, I get the following exception:
java.jang.IllegalArgumentException: Parameter value [new] did not match expected type [com.testing.enums.OrderState (n/a)]
For #3 when I provide an OrderState enum I get the following exception (same as #2):
java.jang.IllegalArgumentException: Parameter value [new] did not match expected type [com.testing.enums.OrderState (n/a)]
Basically I try to send in the enum and get an error but I also get an error when I try to send in a String. Is there anything I can do? What is exactly happening?
i have a column in Database with a String, i.e. '{es:title_es,en:title_en}'
So, I need read it as a Map<String,String>, it will be for I18N.
I need the map with the language as key and label as value.
I use Hiberante 3.3.2 and Java 8 and SQL Server.
Is any chance to obtain this map in the EntityClass? This will be usefull because any relationship with this entity will have the correct representation and not a simple string.
And the behavior keep encapsulated.
Table :
OID CODE TITLES
1 XXX '{es:title_es,en:title_en}'
Entity :
#Entity
#Table(name = "TABLE_NAME")
public class EntityClass {
private Long oid;
private String code;
private String titles; ---> i have this but i want private Map<String, String>
#Column(name = "CODE", nullable = false)
public String getCode() {
return code;
}
#Column(name = "TITLES", nullable = false)
public String getTitles() {
return descriptions;
} ----> this work fine, but here i want to get directly the Map,
}```
I am using envers in my project to audit data.
Now I want to access changed data with audit query.
My pojo class for table is below
#Entity
#Audited(withModifiedFlag=true)
#Table(name = "INSTRUMENT", uniqueConstraints = #UniqueConstraint(columnNames = "INSTRUMENT_NAME"))
public class Instrument implements java.io.Serializable {
private long instrumentId;
private String instrumentName;
private WorkflowState workflowState;
#Id
#Column(name = "INSTRUMENT_ID", unique = true, nullable = false, precision = 22, scale = 0)
public long getInstrumentId() {
return this.instrumentId;
}
public void setInstrumentId(long instrumentId) {
this.instrumentId = instrumentId;
}
#Column(name = "INSTRUMENT_NAME", unique = true, nullable = false, length = 50)
public String getInstrumentName() {
return this.instrumentName;
}
public void setInstrumentName(String instrumentName) {
this.instrumentName = instrumentName;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "STATUS", nullable = false)
public WorkflowState getWorkflowState() {
return this.workflowState;
}
public void setWorkflowState(WorkflowState workflowState) {
this.workflowState = workflowState;
}
}
Now I tried to access this with audit query as
AuditQuery query = reader.createQuery().forRevisionsOfEntity(Instrument.class, false, true)
.add(AuditEntity.property("status").hasChanged());
List list= query.getResultList();
So at the time of accessing getResultList() , Its throwing Exception as follows
SqlExceptionHelper: Fail to convert to internal representation
I figured it out, this is because in my db Instrument.status column is as String data Type. While here I am using Join.
So please tell me how to write query to resolve this problem
PROBLEM is How to write Audit Query if my table has foreign key (class property have join dependency).
Join table WorkflowState discription is as follows
public class WorkflowState implements java.io.Serializable {
private BigDecimal stateId;
private Workflow workflow;
private String stateName;
//getters and setters
And it has a join column too i.e "workflow" .
Use workflowState rather than status. The API is based on you specifying the property name and not the column name.
So this is my first attempt to use JPA and a CriteriaQuery.
I have the following (simplified) entities:
#Entity
#Table(name = "hours")
#XmlRootElement
public class Hours implements Serializable
{
#EmbeddedId
protected HoursPK hoursPK;
#Column(name = "total_hours")
private Integer totalHours;
#JoinColumn(name = "trainer_id", referencedColumnName = "id", nullable = false, insertable = false, updatable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Trainer trainer;
public Hours()
{
}
... getter and setter for the attributes
}
#Embeddable
public class HoursPK implements Serializable
{
#Basic(optional = false)
#Column(name = "date_held", nullable = false)
#Temporal(TemporalType.DATE)
private Date dateHeld;
#Basic(optional = false)
#Column(name = "trainer_id", nullable = false, length = 20)
private String trainerId;
#Column(name = "total_hours")
private Integer totalHours;
public HoursPK()
{
}
... getter and setter ...
}
#Entity
#Table(name = "trainer")
public class Trainer implements Serializable
{
#Id
#Basic(optional = false)
#Column(name = "id", nullable = false, length = 20)
private String id;
#Basic(optional = false)
#Column(name = "firstname", nullable = false, length = 200)
private String firstname;
#Basic(optional = false)
#Column(name = "lastname", nullable = false, length = 200)
private String lastname;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "trainer", fetch = FetchType.LAZY)
private List<Hours> hoursList;
... more attributes, getters and setters
#XmlTransient
public List<Hours> getHoursList() {
return hoursList;
}
public void setHoursList(List<Hours> hoursList) {
this.hoursList = hoursList;
}
}
Essentially a Trainer holds trainings and the hours spent in the trainings are stored in the Hours entity. The PK for the hours table is (trainer_id, date_held) as each trainer only holds one training per day.
I am trying to create a CriteriaQuery to fetch all hours of a trainer for a specific month. This is my attempt:
EntityManagerFactory emf = ...
EntityManager em = emf.createEntityManager();
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Hours> c = builder.createQuery(Hours.class);
Root<Hours> root = c.from(Hours.class);
Calendar cal = Calendar.getInstance();
cal.set(2014, 0, 1);
Expression<Date> from = builder.literal(cal.getTime());
cal.set(2014, 1, 1);
Expression<Date> to = builder.literal(cal.getTime());
Predicate who = builder.equal(root.get(Hours_.trainer), "foobar"); // it fails here
Predicate gt = builder.greaterThanOrEqualTo(root.get(Hours_.hoursPK).get(HoursPK_.dateHeld), from);
Predicate lt = builder.lessThan(root.get(Hours_.hoursPK).get(HoursPK_.dateHeld), to);
c.where(gt,lt,who);
c.orderBy(builder.asc( root.get(Hours_.hoursPK).get(HoursPK_.dateHeld) ));
TypedQuery<Hours> q = em.createQuery(c);
List<Hours> resultList = q.getResultList();
I'm using Hibernate 4.3.1 as the JPA provider and the above code fails with the exception:
Exception in thread "main" java.lang.IllegalArgumentException: Parameter value [foobar] did not match expected type [persistence.Trainer (n/a)]
at org.hibernate.jpa.spi.BaseQueryImpl.validateBinding(BaseQueryImpl.java:885)
Apart from the fact that this seems awfully complicated for a query that even a SQL newbie could write in a few minutes, I have no clue, how I can supply the correct value for the trainer_id column in the hours table in the above query.
I also tried:
Predicate who = builder.equal(root.get("trainer_id"), "foobar");
But that fails with the exception:
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [trainer_id] on this ManagedType [persistence.Hours]
It works, when I obtain an actual entity instance that maps to the "foobar" id:
CriteriaQuery<Trainer> cq = builder.createQuery(Trainer.class);
Root<Trainer> trainerRoot = cq.from(Trainer.class);
cq.where(builder.equal(trainerRoot.get(Trainer_.id), "foobar"));
TypedQuery<Trainer> trainerQuery = em.createQuery(cq);
Trainer foobarTrainer = trainerQuery.getSingleResult();
....
Predicate who = builder.equal(root.get(Hours_.trainer), foobarTrainer);
But that seems a pretty stupid (and slow) way to do it.
I'm sure I'm missing something really obvious here, but I can't find it.
First of all, JPA queries always use class and field names. Never column names. So trying to use trainer_id won't work.
builder.equal(root.get(Hours_.trainer), "foobar");
You're trying to compare the trainer field of the Hours entity with the String "foobar". trainer is of type Trainer. A Trainer can't be equal to a String. Its ID, it firstName, or its lastName, all of type String, can be compared to a String. SO you probably want
builder.equal(root.get(Hours_.trainer).get(Trainer_.id), "foobar");
That said, as you noticed, the Criteria API is extremely complex and leads to unreadable, hard to maintain code. It's useful when you have to dynamically compose a query from several optional criteria (hence the name), but for static queries, you should definitely go with JPQL, which is even easier and shorter than SQL:
select h from Hours h
where h.trainer.id = :trainerId
and h.hoursPK.dateHeld >= :from
and h.hoursPK.dateHeld < :to
order by h.hoursPK.dateHeld
I would strongly advise against using composite keys, especially when one of its components is a functional data (dateHeld) that could have to change. Use numeric, single-column, autogenerated primary keys, and everything will be much simpler, and more efficient.