Display Oracle date in a given format with JSTL using Hibernate - spring

I have a date field in a table in an Oracle(10g) database which is mapped by TemporalType.TIMESTAMP in Hibernate like,
#Column(name = "DISCOUNT_START_DATE")
#Temporal(TemporalType.TIMESTAMP)
private Date discountStartDate;
//Setter and getter.
It fetches dates and displays like the following (which are already inserted into the database),
2012-08-29 01:53:10.0
2012-08-20 02:32:22.0
2012-08-01 14:00:21.0
2012-08-20 13:58:01.0
2012-08-30 04:14:13.0
2012-09-10 16:13:45.0
When I attempt to display them using JSTL like,
<fmt:formatDate pattern="dd-MMM-yyyy hh:mm:ss" value="${row.discountStartDate}"/>
it throws an exception,
javax.el.ELException: Cannot convert 2012-08-29 01:53:10.0 of type
class java.lang.String to class java.util.Date
I have tried to change TemporalType.TIMESTAMP to TemporalType.DATE but didn't work.
Earlier, it was working with XML mapping files (xxx.hbm.xml) like.
<property name="discountStartDate" type="date">
<column length="7" name="DISCOUNT_START_DATE"/>
</property>
but with annotations, it failed. How to apply this format dd-MMM-yyyy hh:mm:ss to display those dates in JSP using JSTL?
EDIT:
The following is the Discount entity class which is mapped with the DSCOUNT table in Oracle (the equals() and the toString() methods have been omitted).
#Entity
#Table(name = "DISCOUNT", catalog = "", schema = "WAGAFASHIONDB")
public class Discount implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "DISCOUNT_ID", nullable = false, precision = 35, scale = 0)
#SequenceGenerator(name = "discountIdSequence", sequenceName = "DISCOUNT_SEQ", allocationSize=1, initialValue=1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "discountIdSequence")
private Long discountId;
#Column(name = "DISCOUNT_CODE", length = 100)
private String discountCode;
#Column(name = "DISCOUNT_PER", precision = 35, scale = 2)
private double discountPer;
#Column(name = "DISCOUNT_START_DATE")
#Temporal(TemporalType.TIMESTAMP)
private Date discountStartDate;
#Column(name = "DISCOUNT_END_DATE")
#Temporal(TemporalType.TIMESTAMP)
private Date discountEndDate;
#OneToMany(mappedBy = "discountId", fetch = FetchType.LAZY)
private Set<OrderTable> orderTableSet;
public Discount() {
}
public Discount(Long discountId) {
this.discountId = discountId;
}
public Long getDiscountId() {
return discountId;
}
public void setDiscountId(Long discountId) {
this.discountId = discountId;
}
public String getDiscountCode() {
return discountCode;
}
public void setDiscountCode(String discountCode) {
this.discountCode = discountCode;
}
public double getDiscountPer() {
return discountPer;
}
public void setDiscountPer(double discountPer) {
this.discountPer = discountPer;
}
public Date getDiscountStartDate() {
return discountStartDate;
}
public void setDiscountStartDate(Date discountStartDate) {
this.discountStartDate = discountStartDate;
}
public Date getDiscountEndDate() {
return discountEndDate;
}
public void setDiscountEndDate(Date discountEndDate) {
this.discountEndDate = discountEndDate;
}
public Set<OrderTable> getOrderTableSet() {
return orderTableSet;
}
public void setOrderTableSet(Set<OrderTable> orderTableSet) {
this.orderTableSet = orderTableSet;
}
}
In JSP, the following loop is used to display data (reducing the code complexity).
<c:forEach var="row" items="${list}" varStatus="loop">
<fmt:formatDate pattern="dd-MMM-yyyy hh:mm:ss" value="${row.discountStartDate}"/>
</c:forEach>
and in Spring DAO, the list which is iterated by the preceding loop is simply retrieved as follows.
#SuppressWarnings("unchecked")
public List<Discount>getList(int currentPage, int rowsPerPage)
{
List<Discount> list = sessionFactory.getCurrentSession()
.createQuery("from Discount order by discountId desc")
.setFirstResult(currentPage)
.setMaxResults(rowsPerPage).list();
for(Discount d:list)
{
System.out.println(d.getDiscountStartDate()+" : "+d.getDiscountEndDate());
}
return list;
}
The preceding loop is just for the sake of demonstration. It displays the dates from the table as follows.
2012-08-29 01:53:10.0 : 2012-08-31 01:53:16.0
2012-08-20 02:32:22.0 : 2012-08-24 02:34:36.0
2012-08-01 14:00:21.0 : 2012-08-31 14:01:30.0
2012-08-20 13:58:01.0 : 2012-08-21 13:58:20.0
2012-08-30 04:14:13.0 : 2012-11-23 16:21:57.0
2012-09-10 16:13:45.0 : 2012-10-26 16:13:39.0
2012-08-22 16:06:23.0 : 2012-08-15 16:06:17.0
2012-08-22 10:35:04.0 : 2012-08-17 10:34:56.0
2012-08-17 10:35:29.0 : 2012-08-10 10:35:35.0
2012-10-08 10:35:56.0 : 2013-03-08 10:35:49.0

Related

What is the ideal way to serialize and deserialize polymorphic entity attribute in spring boot?

I have an Entity class with a column attribute whose type is an abstract class. I want to serialize (object to JSON string) while saving it in the database column and deserialize it into an abstract class (which in turn converts the string to the appropriate concrete class) when it is retrieved from the database.
Here's how I accomplished it:
ProductEntity.java
#Entity
#Table(name="PRODUCT")
#Data
public class ProductEntity{
#Id
#Column(name = "ID", insertable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private BigInteger id;
#Column(name = "DESCRIPTION")
private String description;
#Column(name = "NAME")
private String name;
#Column(name = "PRODUCT_TYPE")
private String productType;
#Column(name = "PRODUCT_SPECS")
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property =
"productType") // -------------------> Map to concrete class based on productType value
#Convert(converter = ObjectConverter.class) // ------------> custom converter
private ProductSpecification productSpec;
}
NOTE : "PRODUCT_SPECS" database column is of JSON type.
ProductSpecification.java
#NoArgsConstructor
#JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS,
include = JsonTypeInfo.As.WRAPPER_OBJECT,
#JsonSubTypes({
#JsonSubTypes.Type(value = ComputerSpecification.class, name = "computer"),
#JsonSubTypes.Type(value = SpeakerSpecification.class, name = "speaker")
})
public abstract class ProductSpecification{ }
ComputerSpecification.java
#Getter
#Setter
#NoArgsConstructor
#JsonTypeName("computer")
public class ComputerSpecification extends ProductSpecification {
String memory;
String displaySize;
String processor;
#JsonCreator
public ComputerSpecification (#JsonProperty("memory") String memory,
#JsonProperty("displaysize") String displaySize,
#JsonProperty("processor") String processor){
super();
this.memory = memory;
this.displaySize = displaySize;
this.processor = processor;
}
}
SpeakerSpecification.java
#Getter
#Setter
#NoArgsConstructor
#JsonTypeName("computer")
public class SpeakerSpecification extends ProductSpecification {
String dimension;
String sensitivity;
String bassPrinciple;
String amplifierPower;
#JsonCreator
public SpeakerSpecification (#JsonProperty("sensitivity") String sensitivity,
#JsonProperty("dimension") String dimension,
#JsonProperty("bassPrinciple") String bassPrinciple,
#JsonProperty("amplifierPower") String amplifierPower){
super();
this.sensitivity = sensitivity;
this.dimension = dimension;
this.bassPrinciple = bassPrinciple;
this.amplifierPower = amplifierPower;
}
}
ObjectConverter.java
NOTE: I am using Jackson ObjectMapper for serialization and deserialization.
public class ObjectConverter implements AttributeConverter<Object, String>{
private final static Logger LOGGER = LoggerFactory.getLogger(ObjectConverter.class);
private static final ObjectMapper mapper;
static {
mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
}
#Override
public String convertToDatabaseColumn(Object attributeObject) {
if (attributeObject == null) {
return "";
}
try {
return mapper.writeValueAsString(attributeObject);
} catch (JsonProcessingException e) {
LOGGER.error("Could not convert to database column", e);
return null;
}
}
#Override
public Object convertToEntityAttribute(String dbColumnValue) {
try {
if (StringUtils.isBlank(dbColumnValue)) {
return null;
}
return mapper.readValue(dbColumnValue, ProductSpecification.class); // ----> mapped to
abstract class
} catch (Exception e) {
LOGGER.error("Could not convert to entity attribute", e);
return null;
}
}
}
Request body 1:
{
"name" : "Bose Bass Module 700 - Black- Wireless, Compact Subwoofer",
"description" : "This wireless, compact subwoofer is designed to be paired with the Bose sound
bar 700 to bring music, movies, and TV to life with Deep, dramatic bass. ",
"productSpec" : {
"sensitivity" : "90 dB",
"bassPrinciple" : "reflex",
"amplifierPower" : "700 watts",
"dimension" : "14-5/16inW x 42-13/16inH x 16-5/16inD"
}
}
This request gets saved in the database column "PRODUCT_SPECS" as :
{".SpeakerSpecification ":{"sensitivity" : "90 dB","bassPrinciple" : "reflex", "amplifierPower" :"700
watts", "dimension" : "14-5/16inW x 42-13/16inH x 16-5/16inD" }}
Now this solution works perfectly fine. The "SpeakerSpecification " key neither appears in the response of GET API call nor in the swagger doc. But having to store the type info in the database really bothers me.
Is there a better approach to this problem where I could avoid having the typeinfo (".SpeakerSpecification ") in the column value?

Performance problem when query a many-to-one relation by jpa

I use spring-boot-data-jpa-2.0 to get data from db. A table has many-to-one relation, and the query speed is too slow, 1000 lines data with foreign key will cost 15s, but by native sql it will cost only 0.07s. I search the issue and found that it is because 1+n problem.
Some solution that says use 'join fetch' in hql can solve. When I use the 'join fetch' in hql, query speed not change.
The system designed as a pure rest service, with spring boot framework.
contract entity
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name = "MA_CONTRACTINFO_B")
public class MaContractinfoB implements java.io.Serializable {
// Fields
private String contractcd;
private MaDepartmentB maDepartmentB;
private Double contractid;
private String contractnm;
private String partya;
private String deputycontract;
private String prjtype;
private Double fundt;
private String bustype;
private String contractstatus;
private Double contractyear;
private String fundratio;
private LocalDateTime signdate;
private String prj;
private LocalDateTime loaddate;
private String udep;
private Double fundaccont;
private Double receipt;
private Double fundacctot;
private Double receiptot;
private String loc;
private String riskasscd;
private String rm;
private String pm;
private Double fundaccrec;
private String adminleader;
private String techleader;
private String leader;
private String progress;
private String cashadmin;
private String timetask;
private String contracttp;
// Constructors
/** default constructor */
public MaContractinfoB() {
}
/** minimal constructor */
public MaContractinfoB(String contractcd) {
this.contractcd = contractcd;
}
/** full constructor */
public MaContractinfoB(String contractcd, MaDepartmentB maDepartmentB, Double contractid, String contractnm,
String partya, String deputycontract, String prjtype, Double fundt, String bustype, String contractstatus,
Double contractyear, String fundratio, LocalDateTime signdate, String prj, LocalDateTime loaddate,
String udep, Double fundaccont, Double receipt, Double fundacctot, Double receiptot, String loc,
String riskasscd, String rm, String pm, Double fundaccrec, String adminleader, String techleader,
String leader, String progress, String cashadmin, String timetask, String contracttp) {
this.contractcd = contractcd;
this.maDepartmentB = maDepartmentB;
this.contractid = contractid;
this.contractnm = contractnm;
this.partya = partya;
this.deputycontract = deputycontract;
this.prjtype = prjtype;
this.fundt = fundt;
this.bustype = bustype;
this.contractstatus = contractstatus;
this.contractyear = contractyear;
this.fundratio = fundratio;
this.signdate = signdate;
this.prj = prj;
this.loaddate = loaddate;
this.udep = udep;
this.fundaccont = fundaccont;
this.receipt = receipt;
this.fundacctot = fundacctot;
this.receiptot = receiptot;
this.loc = loc;
this.riskasscd = riskasscd;
this.rm = rm;
this.pm = pm;
this.fundaccrec = fundaccrec;
this.adminleader = adminleader;
this.techleader = techleader;
this.leader = leader;
this.progress = progress;
this.cashadmin = cashadmin;
this.timetask = timetask;
this.contracttp = contracttp;
}
// Property accessors
#Id
#Column(name = "CONTRACTCD", unique = true, nullable = false)
public String getContractcd() {
return this.contractcd;
}
public void setContractcd(String contractcd) {
this.contractcd = contractcd;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "DEPID")
public MaDepartmentB getMaDepartmentB() {
return this.maDepartmentB;
}
public void setMaDepartmentB(MaDepartmentB maDepartmentB) {
this.maDepartmentB = maDepartmentB;
}
#Column(name = "CONTRACTID", precision = 38, scale = 8)
public Double getContractid() {
return this.contractid;
}
public void setContractid(Double contractid) {
this.contractid = contractid;
}
#Column(name = "CONTRACTNM")
public String getContractnm() {
return this.contractnm;
}
public void setContractnm(String contractnm) {
this.contractnm = contractnm;
}
#Column(name = "PARTYA")
public String getPartya() {
return this.partya;
}
public void setPartya(String partya) {
this.partya = partya;
}
#Column(name = "DEPUTYCONTRACT")
public String getDeputycontract() {
return this.deputycontract;
}
public void setDeputycontract(String deputycontract) {
this.deputycontract = deputycontract;
}
#Column(name = "PRJTYPE")
public String getPrjtype() {
return this.prjtype;
}
public void setPrjtype(String prjtype) {
this.prjtype = prjtype;
}
#Column(name = "FUNDT", precision = 38, scale = 8)
public Double getFundt() {
return this.fundt;
}
public void setFundt(Double fundt) {
this.fundt = fundt;
}
#Column(name = "BUSTYPE")
public String getBustype() {
return this.bustype;
}
public void setBustype(String bustype) {
this.bustype = bustype;
}
#Column(name = "CONTRACTSTATUS")
public String getContractstatus() {
return this.contractstatus;
}
public void setContractstatus(String contractstatus) {
this.contractstatus = contractstatus;
}
#Column(name = "CONTRACTYEAR", precision = 38, scale = 8)
public Double getContractyear() {
return this.contractyear;
}
public void setContractyear(Double contractyear) {
this.contractyear = contractyear;
}
#Column(name = "FUNDRATIO")
public String getFundratio() {
return this.fundratio;
}
public void setFundratio(String fundratio) {
this.fundratio = fundratio;
}
#Column(name = "SIGNDATE", length = 11)
public LocalDateTime getSigndate() {
return this.signdate;
}
public void setSigndate(LocalDateTime signdate) {
this.signdate = signdate;
}
#Column(name = "PRJ")
public String getPrj() {
return this.prj;
}
public void setPrj(String prj) {
this.prj = prj;
}
#Column(name = "LOADDATE", length = 11)
public LocalDateTime getLoaddate() {
return this.loaddate;
}
public void setLoaddate(LocalDateTime loaddate) {
this.loaddate = loaddate;
}
#Column(name = "UDEP")
public String getUdep() {
return this.udep;
}
public void setUdep(String udep) {
this.udep = udep;
}
#Column(name = "FUNDACCONT", precision = 38, scale = 8)
public Double getFundaccont() {
return this.fundaccont;
}
public void setFundaccont(Double fundaccont) {
this.fundaccont = fundaccont;
}
#Column(name = "RECEIPT", precision = 38, scale = 8)
public Double getReceipt() {
return this.receipt;
}
public void setReceipt(Double receipt) {
this.receipt = receipt;
}
#Column(name = "FUNDACCTOT", precision = 38, scale = 8)
public Double getFundacctot() {
return this.fundacctot;
}
public void setFundacctot(Double fundacctot) {
this.fundacctot = fundacctot;
}
#Column(name = "RECEIPTOT", precision = 38, scale = 8)
public Double getReceiptot() {
return this.receiptot;
}
public void setReceiptot(Double receiptot) {
this.receiptot = receiptot;
}
#Column(name = "LOC")
public String getLoc() {
return this.loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
#Column(name = "RISKASSCD")
public String getRiskasscd() {
return this.riskasscd;
}
public void setRiskasscd(String riskasscd) {
this.riskasscd = riskasscd;
}
#Column(name = "RM")
public String getRm() {
return this.rm;
}
public void setRm(String rm) {
this.rm = rm;
}
#Column(name = "PM")
public String getPm() {
return this.pm;
}
public void setPm(String pm) {
this.pm = pm;
}
#Column(name = "FUNDACCREC", precision = 38, scale = 8)
public Double getFundaccrec() {
return this.fundaccrec;
}
public void setFundaccrec(Double fundaccrec) {
this.fundaccrec = fundaccrec;
}
#Column(name = "ADMINLEADER")
public String getAdminleader() {
return this.adminleader;
}
public void setAdminleader(String adminleader) {
this.adminleader = adminleader;
}
#Column(name = "TECHLEADER")
public String getTechleader() {
return this.techleader;
}
public void setTechleader(String techleader) {
this.techleader = techleader;
}
#Column(name = "LEADER", length = 20)
public String getLeader() {
return this.leader;
}
public void setLeader(String leader) {
this.leader = leader;
}
#Column(name = "PROGRESS", length = 1000)
public String getProgress() {
return this.progress;
}
public void setProgress(String progress) {
this.progress = progress;
}
#Column(name = "CASHADMIN", length = 20)
public String getCashadmin() {
return this.cashadmin;
}
public void setCashadmin(String cashadmin) {
this.cashadmin = cashadmin;
}
#Column(name = "TIMETASK", length = 2000)
public String getTimetask() {
return this.timetask;
}
public void setTimetask(String timetask) {
this.timetask = timetask;
}
#Column(name = "CONTRACTTP", length = 50)
public String getContracttp() {
return this.contracttp;
}
public void setContracttp(String contracttp) {
this.contracttp = contracttp;
}
/**
* toString
*
* #return String
*/
#Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(getClass().getName()).append("#").append(Integer.toHexString(hashCode())).append(" [");
buffer.append("contractcd").append("='").append(getContractcd()).append("' ");
buffer.append("maDepartmentB").append("='").append(getMaDepartmentB()).append("' ");
buffer.append("contractid").append("='").append(getContractid()).append("' ");
buffer.append("contractnm").append("='").append(getContractnm()).append("' ");
buffer.append("partya").append("='").append(getPartya()).append("' ");
buffer.append("deputycontract").append("='").append(getDeputycontract()).append("' ");
buffer.append("prjtype").append("='").append(getPrjtype()).append("' ");
buffer.append("fundt").append("='").append(getFundt()).append("' ");
buffer.append("bustype").append("='").append(getBustype()).append("' ");
buffer.append("contractstatus").append("='").append(getContractstatus()).append("' ");
buffer.append("contractyear").append("='").append(getContractyear()).append("' ");
buffer.append("fundratio").append("='").append(getFundratio()).append("' ");
buffer.append("signdate").append("='").append(getSigndate()).append("' ");
buffer.append("prj").append("='").append(getPrj()).append("' ");
buffer.append("loaddate").append("='").append(getLoaddate()).append("' ");
buffer.append("udep").append("='").append(getUdep()).append("' ");
buffer.append("fundaccont").append("='").append(getFundaccont()).append("' ");
buffer.append("receipt").append("='").append(getReceipt()).append("' ");
buffer.append("fundacctot").append("='").append(getFundacctot()).append("' ");
buffer.append("receiptot").append("='").append(getReceiptot()).append("' ");
buffer.append("loc").append("='").append(getLoc()).append("' ");
buffer.append("riskasscd").append("='").append(getRiskasscd()).append("' ");
buffer.append("rm").append("='").append(getRm()).append("' ");
buffer.append("pm").append("='").append(getPm()).append("' ");
buffer.append("fundaccrec").append("='").append(getFundaccrec()).append("' ");
buffer.append("adminleader").append("='").append(getAdminleader()).append("' ");
buffer.append("techleader").append("='").append(getTechleader()).append("' ");
buffer.append("leader").append("='").append(getLeader()).append("' ");
buffer.append("progress").append("='").append(getProgress()).append("' ");
buffer.append("cashadmin").append("='").append(getCashadmin()).append("' ");
buffer.append("timetask").append("='").append(getTimetask()).append("' ");
buffer.append("contracttp").append("='").append(getContracttp()).append("' ");
buffer.append("]");
return buffer.toString();
}
}
department entity
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
#Entity
#Table(name = "MA_DEPARTMENT_B")
public class MaDepartmentB implements java.io.Serializable {
// Fields
private Long id;
private String name;
private String leader;
private Set<MaContractinfoB> maContractinfoBs = new HashSet<MaContractinfoB>(0);
private Set<MaContraimB> maContraimBs = new HashSet<MaContraimB>(0);
// Constructors
/** default constructor */
public MaDepartmentB() {
}
/** minimal constructor */
public MaDepartmentB(Long id) {
this.id = id;
}
/** full constructor */
public MaDepartmentB(Long id, String name, String leader, Set<MaContractinfoB> maContractinfoBs,
Set<MaContraimB> maContraimBs) {
this.id = id;
this.name = name;
this.leader = leader;
this.maContractinfoBs = maContractinfoBs;
this.maContraimBs = maContraimBs;
}
// Property accessors
#Id
#Column(name = "ID", unique = true, nullable = false, precision = 10, scale = 0)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "NAME")
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "LEADER")
public String getLeader() {
return this.leader;
}
public void setLeader(String leader) {
this.leader = leader;
}
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "maDepartmentB")
public Set<MaContractinfoB> getMaContractinfoBs() {
return this.maContractinfoBs;
}
public void setMaContractinfoBs(Set<MaContractinfoB> maContractinfoBs) {
this.maContractinfoBs = maContractinfoBs;
}
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "maDepartmentB")
public Set<MaContraimB> getMaContraimBs() {
return this.maContraimBs;
}
public void setMaContraimBs(Set<MaContraimB> maContraimBs) {
this.maContraimBs = maContraimBs;
}
/**
* toString
*
* #return String
*/
#Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(getClass().getName()).append("#").append(Integer.toHexString(hashCode())).append(" [");
buffer.append("id").append("='").append(getId()).append("' ");
buffer.append("name").append("='").append(getName()).append("' ");
buffer.append("leader").append("='").append(getLeader()).append("' ");
buffer.append("maContractinfoBs").append("='").append(getMaContractinfoBs()).append("' ");
buffer.append("maContraimBs").append("='").append(getMaContraimBs()).append("' ");
buffer.append("]");
return buffer.toString();
}
}
jparepository
public interface MaContractinfoBRepository extends JpaRepository<MaContractinfoB, Long> {
#Query("from MaContractinfoB c join fetch c.maDepartmentB")
List<MaContractinfoB> findAll();
#Query("select contractnm from MaContractinfoB")
List<String> findAllName();
// #Query("from MaContractinfoB c join fetch c.maDepartmentB")
// List test();
}
In MaContractinfoBRepository, When I use findAllName function, it will immediately return 1000 contract names in 0.05s. When I use findAll, it will cost 15s to get 1000 data with department entity, even I add join fetch. But if I get it by native sql in db tool such as navicat, it will cost only 0.07s.
Is any keypoint I missed? How to query the MaContractinfoB table not so slowly?
This is happening because internally with 'fetch' command also hibernate is lazy loading all the rows of Department entity for each Contract id whenever you call the findAll() method. So if there are n rows in Department for 1 Contract and in total there are m Contracts then the total number of calls to the database would be 'm * n'.
One way around this is by using DTO projections. Data Transfer Objects is an easy way to define all the required columns in one query and hit the database for once.
I have found this article useful which shows multiple ways of writing DTO projections.
https://thoughts-on-java.org/dto-projections/
One of the mentioned way is: Using JPQL you can use a constructor expression to define a constructor call with the keyword new followed by the fully classified class name of your DTO and a list of constructor parameters in curly braces.
Something like this:
TypedQuery<ContractWithDepartmentDetails> q = em.createQuery(
"SELECT new com.practice.model.ContractWithDepartmentDetails(c.id, c.name, d.name) FROM contract c JOIN c.department d",
ContractWithDepartmentDetails.class);

How to reference a properties value inside the schema attribute of an entity?

There is an entity :
#Entity
#Table(name = "ITMMASTER" , schema = "TOMCTB")
public class Article {
#Id
#Column(name = "ITMREF_0")
private String code_article;
#Column(name = "ACCCOD_0")
private String acccod;
public String getCode_article() {
return code_article;
}
public void setCode_article(String code) {
this.code_article = code;
}
public String getAcccod() {
return acccod;
}
public void setAcccod(String acccod) {
this.acccod = acccod;
}
}
I want to make the schema attribute to be dynamic depending on a properties file property value , for example : env.schema = TOMEXPL.
How to achieve that ?
I didn't tried it but I guess this should work.
public class DynamicNamingStrategy extends DefaultNamingStrategy {
#Value("db.table.name")
private String name;
#Override
public String tableName(String tableName) {
return name;
}
...
}
SessionFactory sessionFactory;
Configuration config = new AnnotationConfiguration()
.configure("hibernate.cfg.xml")
.setNamingStrategy( new DynamicNamingStrategy() );
sessionFactory = config.buildSessionFactory();
session = sessionFactory.openSession();

Count number of Item using Hibernate/JPA or JdbcTemplate

I am new to Spring/Hibernate/JPA. I have an entity class MovieEntity and MovieVersionEntity. MovieEntity has few details about the movie (like genre of movie) but MovieVersionEntity has more details about it (name, director...). So I want to count the number of movies (MovieVersionEntity) associated to the MovieEntity for the given type.
MovieEntity:
#Entity(name="MovieEntity")
#Table(name="Movie")
public class MovieEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="Id")
private long id;
#Column(name="IsDeleted")
private boolean isDeleted;
#Column(name="ModifiedDate")
#Temporal(TemporalType.TIMESTAMP)
private Date modifiedDate;
#OneToOne()
#JoinColumn(name="MovieTypeId")
private MovieTypeEntity movieTypeEntity;
#OneToMany(mappedBy="movieEntity",optional = false)
private List<MovieVersionEntity> movieVersionEntity;
#Transient
//#Formula("select count(*) from movie_version mv where mv.id=id")
private int childCount;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean isDeleted) {
this.isDeleted = isDeleted;
}
public MovieTypeEntity getMovieTypeEntity() {
return movieTypeEntity;
}
public void setMovieTypeEntity(MovieTypeEntity movieTypeEntity) {
this.movieTypeEntity = movieTypeEntity;
}
public Date getModifiedDate() {
return modifiedDate;
}
public void setModifiedDate(Date modifiedDate) {
this.modifiedDate = modifiedDate;
}
public MovieVersionEntity getMovieVersionEntity() {
return movieVersionEntity;
}
public void setMovieVersionEntity(MovieVersionEntity movieVersionEntity) {
this.movieVersionEntity = movieVersionEntity;
}
public int getChildCount() {
return childCount;
}
public void setChildCount(int childCount) {
this.childCount = childCount;
}
}
MovieVersionEntity
#Entity(name = "MovieVersionEntity")
#Table(name="MovieVersion")
//#EntityListeners(AuditingEntityListener.class)
public class MovieVersionEntity {
#Id()
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="MovieId")
private long movieId;
#NotBlank
#Column(name="MovieName")
private String movieName;
#NotBlank
#Column(name="DirectorName")
private String directorName;
#NotBlank
#Column(name="Description")
private String description;
#Column(name="StopDate")
#Temporal(TemporalType.TIMESTAMP)
private Date stopDate;
#Column(name="DoneWatching")
private boolean doneWatching;
#Column(name="WatchDate")
#Temporal(TemporalType.TIMESTAMP)
//#CreatedDate
private Date watchDate;
#Column(name="ModifiedDate")
#Temporal(TemporalType.TIMESTAMP)
//#LastModifiedDate
private Date modifiedDate;
#ManyToOne(optional = false)
#JoinColumn(name="Id")
private MovieEntity movieEntity;
public String getMovieName() {
return movieName;
}
public void setMovieName(String movieName) {
this.movieName = movieName;
}
public String getDirectorName() {
return directorName;
}
public void setDirectorName(String directorName) {
this.directorName = directorName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getStopDate() {
return stopDate;
}
public void setStopDate(Date stopDate) {
this.stopDate = stopDate;
}
public boolean isDoneWatching() {
return doneWatching;
}
public void setDoneWatching(boolean doneWatching) {
this.doneWatching = doneWatching;
}
public Date getWatchDate() {
return watchDate;
}
public void setWatchDate(Date watchDate) {
this.watchDate = watchDate;
}
public Date getModifiedDate() {
return modifiedDate;
}
public void setModifiedDate(Date modifiedDate) {
this.modifiedDate = modifiedDate;
}
public long getMovieId() {
return movieId;
}
public void setMovieId(long movieId) {
this.movieId = movieId;
}
public MovieEntity getMovieEntity() {
return movieEntity;
}
public void setMovieEntity(MovieEntity movieEntity) {
this.movieEntity = movieEntity;
}
}
I have written a query but I am getting sql error for it
#Query(value = "select m.*, ct.ChildCount" +
"from (" +
"select mv.id, count(movie_id) as ChildCount " +
"from movie_version mv " +
"group by mv.id" +
") as ct join movie m " +
"on ct.id = m.id;",nativeQuery = true)
List<MovieEntity> getMoviesWithCount();
Error
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select mv.id, count(movie_id) as ChildCount from movie_version mv group by mv.id' at line 1
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_60]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_60]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_60]
at java.lang.reflect.Constructor.newInstance(Constructor.java:422) ~[na:1.8.0_60]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:425) ~[mysql-connector-java-5.1.44.jar:5.1.44]
at
Also, I am not sure if this is a right way to do it. Is there any other way I can save the count in the Transient variable. I tried using #Formuala too, but that does not give me 0 count.
Formula:
#Formula("select count(*) from movie_version mv where mv.id=id")
This is the first time I am dealing with Transient variable and I am not sure how it maps to the entity if its not persisted in the db.
However, #Formula worked for me. #Transient and #Formula cannot go together. #Formula is read only so I do not have to worry about the data being persisted.
http://outbottle.com/hibernate-populating-an-unmapped-entity-field-with-count-using-formula/

How does Spring's JPARepository and #Transactional behave together?

I have two methods (in a Spring boot application) that handle an entity. The entity has two fields, both boolean isDefault and isPdfGenerated. The first method (which is called from a controller) changes the isDefault flag when a new entity is created while the second one (called from a #Scheduled annotated method) changes the isPdfGenrated after it generates a pdf file for that entity.
My problem is that sometimes the second method finds entities with the isPdfGenerated flag set to false even though the file has been generated and saved in the database.
Both the methods have the #Transactional annotation and the repository interface for the entity extends JpARepository.
My guess is that the first method loads the entity from the database before the second method does but saves the entity after the second method does its job, thus overriding the isPdfGenerated flag.
Is this possible ? If the answer is yes, how should one handle such cases ? Shouldn't JPARepository handle the case when an entity gets updated from an external source ?
Bellow is some code to better illustrate the situation.
MyController:
#Controller
#RequestMapping("/customers")
public class MyController {
#Autowired
private EntityService entityService;
#RequestMapping(value = "/{id}/changeDefault", method = RequestMethod.POST)
public String changeDefault(#PathVariable("id") Long customerId, #ModelAttribute EntityForm entityForm, Model model) {
Entity newDefaultEntity = entityService.updateDefaultEntity(customerId, entityForm);
if (newDefaultEntity == null)
return "redirect:/customers/" + customerId;
return "redirect:/customers/" + customerId + "/entity/default;
}
}
EntityService:
import org.springframework.transaction.annotation.Transactional;
#Service
public class EntityService {
#Autowired
private EntityRepository entityRepository;
#Autowired
private CustomerRepository customerRepository;
#Transactional
public Entity updateDefaultEntity(Long customerId, submittedData) {
Customer customer = customerRepository.findById(customerId);
if(customer == null)
return customer; // I know there are better ways to do this
Entity currentDefaultEntity = entityRepository.findUniqueByCustomerAndDefaultFlag(customer, true);
if(currentDefaultEntity == null)
return null; // I know there are better ways to do this also
Entity newDefaultEntity = new Entity();
newDefaultEntity.setField1(submittedData.getField1());
newDefaultEntity.setField2(submittedData.getField2());
newDefaultEntity.setCustomer(customer);
oldDefaultEntity.setDefaultFlag(false);
newDefaultEntity.setDefaultFlag(true);
entityRepository.save(newDefaultEntity);
}
#Transactional
public void generatePdfDocument(Entity entity) {
Document pdfDocument = generateDocument(entity);
if(pdfDocument == null)
return;
documentRepository.save(pdfDocument);
entity.setPdfGeneratedFlag(true);
entityRepository.save(entity);
}
}
ScheduledTasks:
#Component
public class ScheduledTasks {
private static final int SECOND_IN_MILLISECONDS = 1000;
private static final int MINUTE_IN_SECONDS = 60;
#Autowired
private EntityRepository entityRepository;
#Autowired
private DocumentService documentService;
#Scheduled(fixedDelay = 20 * SECOND_IN_MILLISECONDS)
#Transactional
public void generateDocuments() {
List<Quotation> quotationList = entityRepository.findByPdfGeneratedFlag(false);
for(Entity entity : entitiesList) {
documentService.generatePdfDocument(entity);
}
}
}
DocumentService:
#Service
public class DocumentService {
#Autowired
private EntityRepository entityRepository;
#Autowired
private DocumentRepository documentRepository;
#Transactional
public void generatePdfDocument(Entity entity) {
Document pdfDocument = generateDocument(entity);
if(pdfDocument == null)
return;
documentRepository.save(pdfDocument);
entity.setPdfGeneratedFlag(true);
entityRepository.save(entity);
}
}
EntityRepository:
#Repository
public interface EntityRepository extends JpaRepository<Entity, Long> {
Entity findById(#Param("id") Long id);
List<Entity> findByPdfGeneratedFlag(#Param("is_pdf_generated") Boolean pdfGeneratedFlag);
Entity findUniqueByCustomerAndDefaultFlag(
#Param("customer") Customer customer,
#Param("defaultFlag") Boolean defaultFlag
);
}
DocumentRepository:
#Repository
public interface DocumentRepository extends JpaRepository<Document, Long> {
Document findById(#Param("id") Long id);
}
Entity:
#Entity
#Table(name = "entities")
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id")
public class Entity {
private Long id;
private boolean defaultFlag;
private boolean pdfGeneratedFlag;
private String field1;
private String field2;
private Customer customer;
public Entity() { }
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "is_default")
public boolean isDefaultFlag() {
return defaultFlag;
}
public void setDefaultFlag(boolean defaultFlag) {
this.defaultFlag = defaultFlag;
}
#Column(name = "is_pdf_generated")
public boolean isPdfGeneratedFlag() {
return pdfGeneratedFlag;
}
public void setPdfGeneratedFlag(boolean pdfGeneratedFlag) {
this.pdfGeneratedFlag = pdfGeneratedFlag;
}
#Column(name = "field_1")
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
#Column(name = "field_2")
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
#ManyToOne
#JoinColumn(name = "customer_id", referencedColumnName = "id", nullable = false)
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Entity quotation = (Entity) o;
return id != null ? id.equals(entity.id) : entity.id == null;
}
#Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
#Override
public String toString() {
return "Entity{" +
"id=" + id +
", pdfGeneratedFlag=" + pdfGeneratedFlag +
", defaultFlag=" + defaultFlag +
", field1=" + field1 +
", field2=" + field2 +
", customer=" + (customer == null ? null : customer.getId()) +
"}";
}
}
I have omitted the other classes because they are either POJOs ( EntityForm ) or the same as other domain model classes ( Document ).
If you're talking about a row on the database that is getting updated by another process after the first process has read it but before it has been updated, then you need to put in some sort of optimistic locking strategy.
This will be handled by the underlying ORM api (e.g. Hibernate or Eclipselink) rather than Spring Data (which will just handle an optimistic locking errors thrown by the ORM).
Have a look at this article. Bear in mind that if you want optimistic locking you need some way of determining a row's version. In JPA this is normally done using a column annotated with the #Version tag.
https://vladmihalcea.com/hibernate-locking-patterns-how-does-optimistic-lock-mode-work/

Resources