Hibernate createCriteria query with annotation based composite primary key - java

In my project, I am having trouble writing a createCriteria query with a composite primary key. My Entity class & DAO method are given below -
#Entity
#Table(name="METRICS")
public class Metrics implements Serializable {
private static final long serialVersionUID = -2580493160757497919L;
#EmbeddedId
protected MetricsID metricsID;
#Column(name="PROJ_PERF")
private String proj_perf;
#Column(name="ANALYSIS")
private String analysis;
public String getProj_perf() {
return proj_perf;
}
public void setProj_perf(String proj_perf) {
this.proj_perf = proj_perf;
}
public String getAnalysis() {
return analysis;
}
public void setAnalysis(String analysis) {
this.analysis = analysis;
}
public MetricsID getMetricsID() {
return metricsID;
}
public void setMetricsID(MetricsID metricsID) {
this.metricsID = metricsID;
}
}
#Embeddable
public class MetricsID implements Serializable {
private static final long serialVersionUID = 4691163770334366543L;
#Column(name="PROJECT_ID")
private String project_id;
#Column(name="METRICS_NO")
private int metrics_no;
public String getProject_id() {
return project_id;
}
public void setProject_id(String project_id) {
this.project_id = project_id;
}
public int getMetrics_n0() {
return metrics_no;
}
public void setMetrics_no(int i) {
this.metrics_no = i;
}
}
#Override
#Transactional
public List<Metrics> viewMetrics(String project_id) throws Exception {
List<Metrics> metrics = (List<Metrics>)sessionFactory.getCurrentSession().
createCriteria(Metrics.class).createAlias("metricsID.project_id", "project_id_alias").
add(Restrictions.eqProperty("project_id_alias.project_id", project_id)).list();
return metrics;
}
The error I am getting is - org.hibernate.QueryException: not an association: metricsID.project_id
I searched for several similar examples, and used alias on the suggestion of one of the search results, but it's my first time using an alias. What am I doing wrong?

Why do you need to use an alias? Have you tried to access directly?
Following this example, this code should work
#Override
#Transactional
public List<Metrics> viewMetrics(String project_id) throws Exception {
List<Metrics> metrics =
(List<Metrics>) sessionFactory.getCurrentSession()
.createCriteria(Metrics.class)
.add(Restrictions.eq("metricsID.project_id", project_id))
.list();
return metrics;
}

Related

MongoDB repository save() do the insert instead of update

I am writing a PUT request API with spring and mongodb. But the save() inserts a new object instead of update the current one.
#Document("Test")
public class Expense {
#Field(name = "name")
private String expenseName;
#Field(name = "category")
private ExpenseCategory expenseCategory;
#Field(name = "amount")
private BigDecimal expenseAmount;
public Expense( String expenseName, ExpenseCategory expenseCategory, BigDecimal expenseAmount) {
this.expenseName = expenseName;
this.expenseCategory = expenseCategory;
this.expenseAmount = expenseAmount;
}
public String getExpenseName() {
return expenseName;
}
public void setExpenseName(String expenseName) {
this.expenseName = expenseName;
}
public ExpenseCategory getExpenseCategory() {
return expenseCategory;
}
public void setExpenseCategory(ExpenseCategory expenseCategory) {
this.expenseCategory = expenseCategory;
}
public BigDecimal getExpenseAmount() {
return expenseAmount;
}
public void setExpenseAmount(BigDecimal expenseAmount) {
this.expenseAmount = expenseAmount;
}
}
This is my reporsitory class
public interface ExpenseRepository extends MongoRepository<Expense, String> {
}
This is my Service class which shows how to update the class.
#Service
public class ExpenseService {
private final ExpenseRepository expenseRepository;
public ExpenseService(ExpenseRepository expenseRepository) {
this.expenseRepository = expenseRepository;
}
public void updateExpense(String id, Expense expense){
Expense savedExpense = expenseRepository.findById(id)
.orElseThrow(() -> new RuntimeException(
String.format("Cannot Find Expense by ID %s", id)));
savedExpense.setExpenseName(expense.getExpenseName());
savedExpense.setExpenseAmount(expense.getExpenseAmount());
savedExpense.setExpenseCategory(expense.getExpenseCategory());
expenseRepository.save(savedExpense);
}
}
This is my controller
#RestController
#RequestMapping("/api/expense")
public class ExpenseController {
private final ExpenseService expenseService;
public ExpenseController(ExpenseService expenseService) {
this.expenseService = expenseService;
}
#PutMapping("/{id}")
public ResponseEntity<Object> updateExpense(#PathVariable String id, #RequestBody Expense expense){
expenseService.updateExpense(id, expense);
return ResponseEntity.ok().build();
}
}
As shown in mongodb compass, mongodb auto generates an _id field for every object. So I do not define a id field or use #id annotation to define a primary for the collection. However, in the service class, expenseRepository.findById(id) retrieves the desired object and update it. Why does save() do the insert instead of update? Many thanks.
JPA Can't find the existing entry as no id field id set. You need to add an id field and set generation type to auto.
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;

#RestController autoserialize POJO's

I have a Spring mvc application, with a #RestController like such:
#RestController
#RequestMapping("levels")
public class LevelController {
private final GetLevelOneCount getLevelOneCount;
private final GetLevelTwoCount getLevelTwoCount;
private final GetLevelThreeCount getLevelThreeCount;
#Inject
public LevelController(GetLevelOneCount getLevelOneCount,
GetLevelTwoCount getLevelTwoCount,
GetLevelThreeCount getLevelThreeCount) {
this.getLevelOneCount = getLevelOneCount;
this.getLevelTwoCount = getLevelTwoCount;
this.getLevelThreeCount = getLevelThreeCount;
}
#GetMapping("/level1/{id}")
public LevelModel levelOne(#PathVariable String id) throws SQLException {
LevelModel levelOneModel = new LevelModel();
levelOneModel.setLevelQuery(getLevelOneCount.execute(id));
levelOneModel.setLevelDirQuery(getLevelOneCount.executeDir(id));
levelOneModel.setLevelDateQuery(getLevelOneCount.executeDate(id));
return levelOneModel;
}
my LevelModel is a POJO with private variables, now i wonder, if this can get serialized to propper JSON with private variables?
package com.pwc.tag.service.levels;
public class LevelModel {
private Long LevelQuery;
private Long LevelDirQuery;
private Long LevelDateQuery;
public Long getLevelQuery() {
return LevelQuery;
}
public void setLevelQuery(Long levelQuery) {
LevelQuery = levelQuery;
}
public Long getLevelDirQuery() {
return LevelDirQuery;
}
public void setLevelDirQuery(Long levelDirQuery) {
LevelDirQuery = levelDirQuery;
}
public Long getLevelDateQuery() {
return LevelDateQuery;
}
public void setLevelDateQuery(Long levelDateQuery) {
LevelDateQuery = levelDateQuery;
}
}
Yes, your object will be serialized to a proper JSON structure including the private field, because of the getters and setters.
If these fields should not be present in the output object, you can add the #JsonIgnore annotation to exclude them from the JSON structure.
P.S. the common approach is to start names of java properties with a lower case letter.

Hibernate entities mapping: Retrieve VARCHAR as boolean

I have a table INCIDENCIA in my database that has a VARCHAR column VISIBLE with two possible values: Y or N matching true or false.
I have it mapped in this entity:
#Entity
public class Incidencia {
private String visible;
//other fields
#Basic
#Column(name = "VISIBLE")
public String getVisible() {
return visible;
}
public void setVisible(String visible) {
this.visible = visible;
}
}
This field is a String since column in database is a VARCHAR, however I would like to retrieve it as java.lang.Boolean with a Y/N deserialization.
Is there any way to do this by Hibernate annotations?
Thanks.
You can create your own mapping type. Something like this:
package es.buena.jamon.type;
public class SpanishBoolean extends AbstractSingleColumnStandardBasicType<Boolean>
implements PrimitiveType<Boolean>, DiscriminatorType<Boolean>
{
private static final long serialVersionUID = 1L;
public static final SpanishBoolean INSTANCE = new SpanishBoolean();
public SpanishBoolean() {
super( CharTypeDescriptor.INSTANCE, new BooleanTypeDescriptor('S', 'N') );
}
#Override
public String getName() {
return "si_no";
}
#Override
public Class getPrimitiveClass() {
return boolean.class;
}
#Override
public Boolean stringToObject(String xml) throws Exception {
return fromString( xml );
}
#Override
public Serializable getDefaultValue() {
return Boolean.FALSE;
}
#Override
public String objectToSQLString(Boolean value, Dialect dialect) throws Exception {
return StringType.INSTANCE.objectToSQLString( value ? "S" : "N", dialect );
}
}
and then register it with the configuration:
Configuration configuration = new Configuration().configure();
configuration.registerTypeOverride(new SpanishBoolean());
and then use it in your entity:
#Type(type="es.buena.jamon.type.SpanishBoolean")
private Boolean visible;
Hope that helps.

LazyInitializationException encountered when using load instead of get with Hibernate

I am using JPA, Hibernate and Spring MVC. In the controller class all the methods works greatly. When I test them in the web browser the public String getModuleFormation(long id) method, that returns an object, and it gives me the following error:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
as a root cause, but yesterday I tried it, and it worked without problem in the localhost:45045/GestionModules/detail/xx URL.
What could cause this problem?
My detail.jsp:
<c:if test="${!empty detailModule}">
${detailModule.idModule}
${detailModule.libModule}
</c:if>
POJO Class + JPA :
#Entity
#Table(name="ModuleFormation")
public class ModuleFormation {
private long idModule;
private String libModule;
public ModuleFormation() {
// TODO Auto-generated constructor stub
}
public ModuleFormation(String libModule) {
this.libModule = libModule;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "seqModule")
#SequenceGenerator(name="seqModule", sequenceName = "seqModuleFormation")
#Column(name="idModule")
public long getIdModule() {
return this.idModule;
}
public void setIdModule(long idModule) {
this.idModule = idModule;
}
#Column(name="libModule", nullable=false, length = 100)
public String getLibModule() {
return this.libModule;
}
public void setLibModule(String libModule) {
this.libModule = libModule;
}
}
DAO Class :
#Repository
public class ModuleFormationDAOImpl implements ModuleFormationDAO {
#Autowired
private SessionFactory sessionFactory;
public void ajouterModuleFormation(ModuleFormation module) {
sessionFactory.getCurrentSession().save(module);
}
public void supprimerModuleFormation(long idModule) {
ModuleFormation module = (ModuleFormation) sessionFactory.getCurrentSession().load(ModuleFormation.class, idModule);
if(module != null)
sessionFactory.getCurrentSession().delete(module);
}
public List<ModuleFormation> listModuleFormation() {
return sessionFactory.getCurrentSession().createQuery("from ModuleFormation")
.list();
}
public ModuleFormation getModuleFormation(long idModule) {
return (ModuleFormation) sessionFactory.getCurrentSession().load(ModuleFormation.class, idModule);
}
public void majModuleFormation(ModuleFormation module) {
sessionFactory.getCurrentSession().merge(module);
}
}
Service Class :
#Service
public class ModuleFormationServiceImpl implements ModuleFormationService {
#Autowired
private ModuleFormationDAO moduleDao;
#Transactional
public void ajouterModuleFormation(ModuleFormation module) {
moduleDao.ajouterModuleFormation(module);
}
#Transactional
public void supprimerModuleFormation(long idModule) {
moduleDao.supprimerModuleFormation(idModule);
}
#Transactional
public List<ModuleFormation> listModuleFormation() {
return moduleDao.listModuleFormation();
}
#Transactional
public ModuleFormation getModuleFormation(long idModule) {
return moduleDao.getModuleFormation(idModule);
}
#Transactional
public void majModuleFormation(ModuleFormation module) {
moduleDao.majModuleFormation(module);
}
}
Controller Class :
#Controller
public class ModuleFormationController {
#Autowired
private ModuleFormationService moduleService;
#RequestMapping("/module")
public String listModulesFormations(Map<String, Object> map) {
map.put("module", new ModuleFormation());
map.put("moduleList", moduleService.listModuleFormation());
return "module";
}
#RequestMapping(value = "/ajouter", method = RequestMethod.POST )
public String ajouterModuleFormation(#ModelAttribute("module")
ModuleFormation module,BindingResult result) {
moduleService.ajouterModuleFormation(module);
return "redirect:/module";
}
#RequestMapping(value = "/supprimer/{idModule}")
public String supprimerModuleFormation(#PathVariable("idModule")
long idModule) {
moduleService.supprimerModuleFormation(idModule);
return "redirect:/module";
}
#RequestMapping(value= "/detail/{idModule}")
public String getModuleFormation(#PathVariable("idModule")
long idModule,Map<String, Object> map) {
map.put("detailModule", moduleService.getModuleFormation(idModule));
return "/detail";
}
#RequestMapping(value= "/detail/modifier", method = RequestMethod.POST )
public String majModuleFormation(#ModelAttribute("detailModule")
ModuleFormation module, BindingResult result) {
moduleService.majModuleFormation(module);
return "detail/{idModule}";
}
}
The Javadoc on the Hibernate Session#load(Class, Serializable) method says:
Return the persistent instance of the given entity class with the given identifier,
assuming that the instance exists. This method might return a proxied instance that
is initialized on-demand, when a non-identifier method is accessed.
When you access a property on the object in your JSP the session which loaded the object has been closed.
Use Session#get(Class, Serializable) to ensure that you don't load a proxy.
Instead of sessionFactory.getCurrentSession().load(ModuleFormation.class, idModule), have you tried sessionFactory.getCurrentSession().get(ModuleFormation.class, idModule)?

How to get Hibernate JPA to manage list index automagically?

How do I setup a basic OneToMany relationship using a List and get Hibernate JPA to manage the sequence index number of the list automagically? Can this be done?
This is my test case (more or less);
#Table(name="Policy_Root")
public class PolicyRoot extends BaseDomainModel {
private List<Policy> policyList = new ArrayList<Policy>();
#OneToMany(targetEntity=Policy.class, mappedBy="policyRoot", cascade=CascadeType.ALL)
#IndexColumn(name="policy_sequence", base=0, nullable=false)
public List<Policy> getPolicyList() {
return policyList;
}
public void setPolicyList(List<Policy> policyList) {
this.policyList = policyList;
}
public void addPolicy(Policy policy) {
policyList.add(policy);
policy.setPolicyRoot(this);
}
public void addPolicy(int sequence, Policy policy) {
policyList.add(sequence, policy);
policy.setPolicyRoot(this);
}
}
#Entity()
#Table(name="Policy")
public class Policy extends BaseDomainModel {
/** The position of this policy record within the list of policy's belong to the parent PolicyRoot */
private int policySequence;
/** Birectional pointer to parent */
private PolicyRoot policyRoot;
#Column(name="policy_sequence")
public int getPolicySequence() {
return policySequence;
}
public void setPolicySequence(int policySequence) {
this.policySequence = policySequence;
}
#ManyToOne
#JoinColumn(name="policy_root_oid", nullable=false)
public PolicyRoot getPolicyRoot() {
return policyRoot;
}
public void setPolicyRoot(PolicyRoot policyRoot) {
this.policyRoot = policyRoot;
}
}
#Test
public void testCreation() {
Policy policy1 = new Policy();
Policy policy2 = new Policy();
// Uncomment the following and the test case works - but I don't want to manage the sequence numbers
//policy2.setPolicySequence(1);
PolicyRoot policyRoot = new PolicyRoot();
policyRoot.addPolicy(policy1);
policyRoot.addPolicy(policy2);
ServiceImplFacade.getPersistenceFacade().persistSingleItem(policyRoot);
Long oid = policyRoot.getOid();
PolicyRoot policyRootFromDB = ServiceImplFacade.getPersistenceFacade().getEntityManager().find(PolicyRoot.class, oid);
assertEquals(2, policyRootFromDB.getPolicyList().size());
}
If I uncomment the policy2.setPolicySequence(1); line then the test case passes, but I don't think I need to do this. I want Hibernate to do this for me. My understanding is that it can, but if it can't then knowing that it can't would be a good answer as well.
I've tried various combinations of setting nullable, insertable and updateable but I may have missed one.
Is this possible? - If so how?
Found the answer, - it was around getting the right combinations of nullable and insertable. Also had to make the "child index" at Integer so that it could be nullable, and there's also an "optional" flag in the following as well.
public class PolicyRoot extends BordereauxBaseDomainModel {
private List<Policy> policyList = new ArrayList<Policy>();
#OneToMany(cascade=CascadeType.ALL)
#IndexColumn(name="policy_sequence", nullable=false, base=0)
#JoinColumn(name="policy_root_oid", nullable=false)
public List<Policy> getPolicyList() {
return policyList;
}
public void setPolicyList(List<Policy> policyList) {
this.policyList = policyList;
}
}
public class Policy extends BordereauxBaseDomainModel {
/** The position of this policy record within the list of policy's belong to the parent PolicyRoot */
private Integer policySequence;
/** Birectional pointer to parent */
private PolicyRoot policyRoot;
#Column(name="policy_sequence", insertable=false, updatable=false)
public Integer getPolicySequence() {
return policySequence;
}
public void setPolicySequence(Integer policySequence) {
this.policySequence = policySequence;
}
#ManyToOne(optional=false)
#JoinColumn(name="policy_root_oid", insertable=false, updatable=false, nullable=false)
public PolicyRoot getPolicyRoot() {
return policyRoot;
}
public void setPolicyRoot(PolicyRoot policyRoot) {
this.policyRoot = policyRoot;
}
}
Found the answers on the following page after searching Google for a while.
http://opensource.atlassian.com/projects/hibernate/browse/HHH-4390
Do something like this:
#Entity
class Parent {
#OneToMany
#IndexColumn(name = "index_column")
List<Child> children;
}
#Entity
class Child {
#ManyToOne
Parent parent;
#Column(name = "index_column")
Integer index;
#PrePersist
#PreUpdate
private void prepareIndex() {
if (parent != null) {
index = parent.children.indexOf(this);
}
}
}
I'm going to post this answer since I recently had the same issue and this question, although outdated, keeps coming up in the researches.
The #IndexColumn annotation has been deprecated a long time ago and in its place it is best recommended using the #OrderColumn annotation. The second annotation not only simplifies its syntax without having to specify the base attribute, but it also avoids declaring an extra field in the detail class, in this case the policySequence field within the Policy class.
Here is the updated version of the previous snippet:
#Table(name="Policy_Root")
public class PolicyRoot extends BaseDomainModel {
private List<Policy> policyList = new ArrayList<Policy>();
#OneToMany(targetEntity=Policy.class, mappedBy="policyRoot", cascade=CascadeType.ALL)
#OrderColumn(name="policy_sequence", nullable=false)
public List<Policy> getPolicyList() {
return policyList;
}
public void setPolicyList(List<Policy> policyList) {
this.policyList = policyList;
}
public void addPolicy(Policy policy) {
policyList.add(policy);
policy.setPolicyRoot(this);
}
public void addPolicy(int sequence, Policy policy) {
policyList.add(sequence, policy);
policy.setPolicyRoot(this);
}
}
#Entity()
#Table(name="Policy")
public class Policy extends BaseDomainModel {
//No need to declare the policySequence field
/** Birectional pointer to parent */
private PolicyRoot policyRoot;
#Column(name="policy_sequence")
public int getPolicySequence() {
return policySequence;
}
public void setPolicySequence(int policySequence) {
this.policySequence = policySequence;
}
#ManyToOne
#JoinColumn(name="policy_root_oid", nullable=false)
public PolicyRoot getPolicyRoot() {
return policyRoot;
}
public void setPolicyRoot(PolicyRoot policyRoot) {
this.policyRoot = policyRoot;
}
}

Categories