Using a drop-down list in a model driven action class in Struts2 - drop-down-menu

I'm populating an <s:select> from database. The action class is model-driven.
#Namespace("/admin_side")
#ResultPath("/WEB-INF/content")
#ParentPackage(value="struts-default")
public final class TestAction extends ActionSupport implements Serializable, ValidationAware, Preparable, ModelDriven<Transporter>
{
#Autowired
private final transient SharableService sharableService=null;
private static final long serialVersionUID = 1L;
private Transporter transporter; //Getter and setter
private Long transporterId; //Getter and setter.
private List<Transporter> transporters; //Getter only.
#Action(value = "Test",
results = {
#Result(name=ActionSupport.SUCCESS, location="Test.jsp"),
#Result(name = ActionSupport.INPUT, location = "Test.jsp")},
interceptorRefs={#InterceptorRef(value="defaultStack", params={"validation.validateAnnotatedMethodOnly", "true", "validation.excludeMethods", "load"})})
public String load() throws Exception
{
return ActionSupport.SUCCESS;
}
#Validations(
requiredFields={#RequiredFieldValidator(fieldName="transporterId", type= ValidatorType.FIELD, key = "transporter.required")})
#Action(value = "testInsert",
results = {
#Result(name=ActionSupport.SUCCESS, location="Test.jsp", params={"namespace", "/admin_side", "actionName", "Test"}),
#Result(name = ActionSupport.INPUT, location = "Test.jsp")},
interceptorRefs={#InterceptorRef(value="defaultStack", params={"validation.validateAnnotatedMethodOnly", "true"})})
public String insert() {
System.out.println("Selected item in the drop box : "+transporterId);
return ActionSupport.SUCCESS;
}
#Override
public void prepare() throws Exception {
transporters=sharableService.getTransporterList();
}
#Override
public Transporter getModel() {
return transporter;
}
}
and the following is <s:select> :
<s:select id="transporterId"
name="transporterId"
list="transporters"
value="transporterId"
listKey="transporterId"
listValue="transporterName"
headerKey="" headerValue="Select"
listTitle="transporterName"/>
This works perfectly.
I need this <s:select> in another action class which implements ModelDriven<ZoneTable>.
The table structure is simple, transporter->zone_table->country->state->city. There exists a one-to-many relationship between these tables.
How can we have a model driven action class implementing ModelDrven<ZoneTable> in which Transporter can be mapped to <s:select>, something like?
#Namespace("/admin_side")
#ResultPath("/WEB-INF/content")
#ParentPackage(value="struts-default")
public final class ZoneAction extends ActionSupport implements Serializable, ValidationAware, Preparable, ModelDriven<ZoneTable>
{
#Autowired
private final transient ZoneService zoneService=null;
#Autowired
private final transient SharableService sharableService=null;
private ZoneTable entity=new ZoneTable(); //Getter and setter.
private Long transporterId; //Getter and setter.
private List<Transporter> transporters; //Getter only.
#Override
public ZoneTable getModel() {
return entity;
}
#Override
public void prepare() throws Exception {
transporters=sharableService.getTransporterList();
}
}
Doing like this doesn't work. It doesn't set the value of transporterId upon submission, since the action class is implementing ModelDriven<ZoneTable> and not ModelDriven<Transporter> like the first case.
Is this possible using the model driven approach?
EDIT:
ZoneTable.java
public class ZoneTable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "zone_id", nullable = false)
private Long zoneId;
#Column(name = "zone_name", length = 45)
private String zoneName;
#JoinColumn(name = "transporter_id", referencedColumnName = "transporter_id")
#ManyToOne(fetch = FetchType.LAZY)
private Transporter transporterId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "zoneTable", fetch = FetchType.LAZY)
private Set<ZoneCharge> zoneChargeSet;
#OneToMany(mappedBy = "zoneId", fetch = FetchType.LAZY)
private Set<Country> countrySet;
//Getters and setters + constructors.
}
Zone.jsp
<s:form namespace="/admin_side" action="Zone" validate="true" id="dataForm" name="dataForm" cssClass="search_form general_form">
<s:label key="label.zone.name" for="zone"/>
<s:textfield id="zoneName" name="zoneName" cssClass="validate[required, maxSize[45], minSize[2]] text-input text"/>
<s:fielderror fieldName="zoneName"/>
<s:label key="label.transporter.name" for="transporterId"/>
<s:select id="transporterId" name="transporterId" list="transporters" value="transporterId" listKey="transporterId" listValue="transporterName" headerKey="" headerValue="Select" listTitle="transporterName"/>
<s:fielderror fieldName="transporterId"/>
<s:text name="label.submit"/>
<s:submit id="btnSubmit" name="btnSubmit" value="Submit" action="AddZone"/>
</s:form>
Since this post has already a lot of code, I'm not posting the action class ZoneAction.java here. In case, it is needed, it is available here.

You need a converter to convert transporterId to Transporter Object. It goes like this:
package com.converter;
public class TransporterConverter extends StrutsTypeConverter {
#Override
public Object convertFromString(Map map, String[] strings, Class type) {
String value = strings[0]; // The value of transporterId submitted from the jsp
if (value != null && value.length() > 0) {
try {
Long longVal = Long.valueOf(value);
//Integer intVal = Integer.valueOf(value);
if (type == Transporter.class) {
Transporter data = find_transporter_from_the_back_by_transporter_id_using_longVal;
return data;
}
} catch (Exception ex) {}
}
return null;
}
#Override
public String convertToString(Map map, Object o) {
if ((o instanceof Transporter)) {
Transporter data = (Transporter) o;
//return the id of the Transporter Object
}
return null;
}
}
The next thing to do is to map this class in a file called xwork-conversion.properties. This file must reside in your classpath i.e. in classes directory. Enter the following entries in xwork-conversion.properties
package_of_transporter_class.Transporter=com.converter.TransporterConverter
I have not tested it, but I think it should work.
If you need more information on how type converters work, follow this url.

Related

Spring JPA Transaction ID

I have added an attribute to all my entities - transaction id - which is a sequence generated value that I bump up once in each transaction.
I also store the transaction id with user and start/end times so I have an audit trail for every change in the database.
What is the best way to handle storing a complete graph, where I basically only want to apply the transaction id to those entities that are actually dirty?
I can put a #PrePersist and #PreUpdate on the transaction id column, but how do I retrieve the value for the current transaction id? Is there a way to store and retrieve a value on the transaction object or other JPA controller? Do I need to use a ThreadLocal solution?
Ok, here is what I did. It seems to work in all of the use cases, though I have not done any performance testing, etc. If anyone sees anything that may be non-optimal or may fail in certain situations, please point it out.
Here is the base service class that all #Service implementations must extend:
public class BaseService
{
private final ActivityService activityService;
private final ApplicationEventPublisher applicationEventPublisher;
public static ThreadLocal<Activity> transaction = new ThreadLocal<>();
public BaseService(ActivityService activityService, ApplicationEventPublisher applicationEventPublisher)
{
this.activityService = activityService;
this.applicationEventPublisher = applicationEventPublisher;
}
Object executeWithinActivity(Updater updater)
{
boolean startedLocally = false;
try
{
if (transaction.get() == null)
{
startedLocally = true;
Activity activity = activityService.startTransaction();
transaction.set(activity);
}
return updater.execute(transaction.get());
}
finally
{
if (startedLocally)
{
applicationEventPublisher.publishEvent(new TransactionEvent());
Activity activity = transaction.get();
activityService.endTransaction(activity);
}
}
}
protected interface Updater
{
Object execute (Activity activity);
}
static class TransactionEvent
{
}
}
Activity is the entity that represents the stored transaction id:
#Entity
#Getter #Setter
#Table(name = "transactions", schema = "public", catalog = "euamdb")
public class Activity
{
#Id
#Column(name = "transaction_id", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tx_generator")
#SequenceGenerator(name = "tx_generator", sequenceName = "transaction_seq", allocationSize = 1)
private long transactionId;
#Basic
#Column(name = "user_id", length = 24)
private String userId;
#Basic
#Column(name = "transaction_start")
#CreationTimestamp
private Date transactionStart;
#Basic
#Column(name = "transaction_end")
#UpdateTimestamp
private Date transactionEnd;
#Override
public boolean equals(Object o)
{
if (this == o) return true;
if (!(o instanceof Activity)) return false;
Activity that = (Activity) o;
return transactionId == that.transactionId;
}
#Override
public int hashCode()
{
return Long.hashCode(transactionId);
}
}
ActivityService (which does not extend BaseService):
#Service
public class ActivityService
{
private final ActivityRepository activityRepository;
private final AuthUserService authService;
#Autowired
public ActivityService(ActivityRepository activityRepository, AuthUserService authService)
{
this.activityRepository = activityRepository;
this.authService = authService;
}
#Transactional
public Activity startTransaction()
{
Activity activity = new Activity();
activity.setTransactionStart(new Date());
activity.setUserId(authService.getAuthenticatedUserId());
activityRepository.save(activity);
return activity;
}
#Transactional
public void endTransaction(Activity activity)
{
activity.setTransactionEnd(new Date());
activityRepository.save(activity);
}
}
The base entity class for all entities (excepting Activity):
#MappedSuperclass
#Getter #Setter
public class BaseEntity
{
#Basic
#Column(name = "transaction_id")
private Long transactionId;
#PrePersist
#PreUpdate
public void setupTransaction ()
{
ThreadLocal<Activity> transaction = BaseService.transaction;
Activity activity = transaction.get();
long transactionId = activity.getTransactionId();
setTransactionId(transactionId);
}
}
An example of a service:
#Service
public class OrganizationService extends BaseService
{
private final OrgUserRepository orgUserRepository;
private final UserService userService;
#Autowired
public OrganizationService(ActivityService activityService,
OrgUserRepository orgUserRepository,
UserService userService,
ApplicationEventPublisher applicationEventPublisher)
{
super(activityService, applicationEventPublisher);
this.orgUserRepository = orgUserRepository;
this.userService = userService;
}
#Transactional
public OrgUser save(User user, OrgUser orgUser)
{
return (OrgUser) executeWithinActivity(activity ->
{
orgUser.setUser(userService.save(user));
return orgUserRepository.save(orgUser);
});
}
}
UserService also will extend BaseService and the save(OrgUser) method will also executeWithinActivity.
Finally, the commit listener:
#Component
public class AfterCommitListener
{
#TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
public void doAfterTxComplete(BaseService.TransactionEvent event)
{
BaseService.transaction.remove();
}
}

marshall attributes inside XML elements with JAXB

I work with Spring JPA and have the following entity:
#Entity
#Table(name = Constants.ENTITY_TABLE_PREFIX + "ENTRY")
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "monObj_info")
public class EntryXML implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
#XmlAttribute
private long id;
#Column(name = "ip_address", nullable = true)
#XmlElement
private String ip_address;
#Column(name = "network_element_name", nullable = false)
#XmlElement
private String network_element_name;
public EntryXML() {}
public EntryXML(long id, String ip_address, String network_element_name) {
super();
this.id = id;
this.ip_address = ip_address;
this.network_element_name = network_element_name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getIp_address() {
return ip_address;
}
public void setIp_address(String ip_address) {
this.ip_address = ip_address;
}
public String getNetwork_element_name() {
return network_element_name;
}
public void setNetwork_element_name(String network_element_name) {
this.network_element_name = network_element_name;
}
}
and the endpoint:
#RestController
public class EntryXMLEndpoint {
#Autowired
private IEntryXMLService service;
#RequestMapping(value = "/restxml", produces = { "application/xml" })
public EntryXML findEntries() {
EntryXML record = service.findById(1);
return record;
}
}
Now the requested response is:
<monObj_info id="1">
<atribute name="ip_address" value="xx.xxx.xxx.x"/>
<atribute name="network_element_name" value="xxxxxx"/>
</monObj_info>
Of course what I get is :
<monObj_info id="1">
<ip_address>xx.xxx.xxx.x</ip_address>
<network_element_name>xxxxxx</network_element_name>
</monObj_info>
I read similar posts , but the problem is I cannot create a List with the required elements inside my Entity Class, since it will not map with any column in the respective table, any suggestions?
You can achieve your goal in a straight-forward but somewhat hackish way.
Since you don't want the ip_address and network_element_name properties
to be marshalled and unmarshalled directly, you need to remove their #XmlElement annotation
and add #XmlTransient.
Instead, you want some <atribute name="..." value="..." /> elements marshalled and unmarshalled.
Therefore you need to add the following things to your EntryXML class:
an attributes property holding a list of attributes.
It is annotated with #XmlElement so that it will be part of XML marshalling and unmarshalling.
It is annotated with #Transient so that it will not be part of database persistence.
a simple helper class Attribute for holding name and value.
name and value are annotated with #XmlAttribute so that they will be part of XML marshalling and unmarshalling.
a Marshal Event Callback (beforeMarshal)
for doing the conversion from ip_address and network_element_name
to the attributes list.
an Unmarshal Event Callback (afterUnmarshal)
for doing the opposite conversion.
#XmlElement(name = "atribute")
#Transient // from package javax.persistence
private List<Attribute> attributes;
// there is no need for getAttributes and setAttributes methods
private static class Attribute {
#SuppressWarnings("unused") // called by the unmarshaller
Attribute() {
}
Attribute(String name, String value) {
this.name = name;
this.value = value;
}
#XmlAttribute
private String name;
#XmlAttribute
private String value;
}
#SuppressWarnings("unused") // this method is called only by the marshaller
private boolean beforeMarshal(Marshaller marshaller) {
attributes = new ArrayList<>();
attributes.add(new Attribute("ip_address", ip_address));
attributes.add(new Attribute("network_element_name", network_element_name));
return true;
}
#SuppressWarnings("unused") // this method is called only by the unmarshaller
private void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
if (attributes != null) {
for (Attribute attribute : attributes) {
switch (attribute.name) {
case "ip_address":
ip_address = attribute.value;
break;
case "network_element_name":
network_element_name = attribute.value;
break;
}
}
}
}
Then the XML output will look like this:
<monObj_info id="1">
<atribute name="ip_address" value="xx.xxx.xxx.x"/>
<atribute name="network_element_name" value="xxxxxx"/>
</monObj_info>

Spring Joining table insert. Skip adding records to other table in join. Persist data only to main and link table

Help me in Entity design with the for following E-R.
Here I need to insert entries into REQUEST table and REQ_TYPE_MAP table. There shouldn't be any entry to TYPE_MAST table.
TYPE_MAST contains master data that are loaded once. I need to map the master data to a request.
But the below approach that I use had adding entries to TYPE_MAST table too. How can I avoid that.
Request Entity
#Entity
#Table(name="REQUEST")
public class Request implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="REQUEST_ID")
private long requestId;
#Column(name="CIVILID")
private Long civilId;
#Column(name="MOBILE_NO")
private Long mobileNo;
#ManyToMany(cascade ={ CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name="REQ_TYPE_MAP",
joinColumns = #JoinColumn(name="REQUEST_ID"),
inverseJoinColumns = #JoinColumn(name="TYPE_ID")
)
private Set<MsaDisabScreenRequest> disabilities= new HashSet<MsaDisabScreenRequest>()
/**
getters & setters
equals() & hashCode()
**/
}
Type Mast Entity
#Entity
#Table(name="TYPE_MAST")
public class MsaDisabMaster implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="TYPE_ID")
private long disTypeId;
#Column(name="NAME")
private String name;
/**
getters & setters
equals() & hashCode()
**/
}
Request DTO
public class RequestDto implements Serializable {
private static final long serialVersionUID = 1L;
private long requestId;
private Long civilId;
private Long mobileNo;
private Set<MsaDisabScreenRequestDto> disabilities= new HashSet<MsaDisabScreenRequestDto>()
/**
getters & setters
equals() & hashCode()
**/
}
Type Mast Dto
public class MsaDisabMasterDto implements Serializable {
private static final long serialVersionUID = 1L;
private long disTypeId;
private String name;
/**
getters & setters
equals() & hashCode()
**/
}
Using spring JPA to persist.
#Service code relevant to save(let me know if you required more details).
Service
#SuppressWarnings("all")
public class ReqServiceImpl implements ReqService {
#Autowired
MsaRepository msaRepository;
#Autowired
private OrikaBeanMapper mapper;
#Override
#Transactional(rollbackOn = BusinessException.class)
public long saveScreeningRequest(RequestDto requestDto) throws BusinessException {
try {
// mapping
Request request = mapper.map(requestDto,Request.class);
Request res = reqRepository.save(request);
if (msaDisabScreenRes != null) {
return res.getRequestId();
} else {
//throw exception
}
} catch (DataAccessException e) {
//throw exception
}
}
}
First of all, since MsaDisabMaster is a permanent data, I recommend to make it immutable.
As MsaDisabMaster objects are already persisted then you must remove cascading from its reference in Request.
In your service you should 'attach' all MsaDisabMaster that are contained in RequestDto to the resulted Request. You can do this with getOne method of your MsaDisabMaster repository, something like this:
requestDto.getDisabilities()
.forEach(m -> request.getDisabilities().add(msaDisabMasterRepo.getOne(m.getDisTypeId())));
Then you can save request using save() method of the Request repository.
Please check this example:
https://www.mkyong.com/hibernate/hibernate-many-to-many-relationship-example-annotation/
This should work for you
#ManyToMany(fetch = FetchType.PERSIST, cascade = CascadeType.MERGE)
#JoinTable(name = "REQ_TYPE_MAP",joinColumns = {
#JoinColumn(name = "REQUEST_ID", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "TYPE_ID",
nullable = false, updatable = false) })

Spring/JPA: composite Key find returns empty elements [{}]

I have build my data model using JPA and am using Hibernate's EntityManager to access the data. I am using this configuration for other classes and have had no problems.
The issue is that I created an entity with a composite primary key (the two keys are foreign keys) , adding elements works perfectly I checked it in database but I am not able to retrieve the populated row from database.
For example if I query "FROM Referentiel" to return a list of all referentiels in the table, I get this [{},{}] my list.size() has the proper number of elements (2), but the elements are null.
The entity:
#Entity
#Table(name = "Et_referentiel")
public class Referentiel implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#ManyToOne
#JoinColumn(name = "id_projet")
private Projet projet;
#Id
#ManyToOne
#JoinColumn(name = "id_ressource")
private Ressource ressource;
#Column(name = "unite", nullable = false)
private String unite;
}
here is my controller getList method:
#PostMapping(value = "/list", consumes = { MediaType.APPLICATION_JSON_UTF8_VALUE })
public List<Referentiel> listReferentiel(#RequestBody Long idProjet) {
List<Referentiel> referentiel = referentielService.listReferentiel(idProjet);
return referentiel;
}
and here is my dao methods:
#Autowired
private EntityManager em;
#Override
public void ajouterReferentiel(Referentiel ref) {
em.persist(ref);
em.flush();
}
#SuppressWarnings("unchecked")
#Override
public List<Referentiel> listReferentiel(Long idProjet) {
Query query = em.createQuery("Select r from Referentiel r where r.projet.idProjet=:arg1");
query.setParameter("arg1", idProjet);
em.flush();
List<Referentiel> resultList = query.getResultList();
return resultList;
}
Any help is greatly appreciated.
Try creating a class representing your composite key:
public class ReferentielId implements Serializable {
private static final long serialVersionUID = 0L;
private Long projet; // Same type than idProjet, same name than inside Referentiel
private Long ressource; // Same type than idRessource (I guess), same name than inside Referentiel
// Constructors, getters, setters...
}
And assign it to your entity having that composite key.
#Entity
#IdClass(ReferentielId.class) // <- here
#Table(name = "Et_referentiel")
public class Referentiel implements Serializable {
// ...
}
Notice that it is required to have a class representing your composite keys, even if that does not help in your problem.

Hibernate transaction and session with multiple save

Thanks, let me completely change it.
Using:
Spring Boot, Hibernate JPA
I have created a link table with a composite primary key across all 3 columns(event_attendee_link_program)
I used the JPA tools in STS IDE to generate Entities from my tables and it came up with the below code. I removed some of the columns to save space.
EventAttendee.java
#Entity
#Table(name="event_attendee")
#NamedQuery(name="EventAttendee.findAll", query="SELECT e FROM EventAttendee e")
public class EventAttendee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="attendee_id")
private long attendeeId;
//bi-directional many-to-one association to EventAttendeeLinkProgram
#OneToMany(mappedBy="eventAttendee")
private List<EventAttendeeLinkProgram> eventAttendeeLinkPrograms;
public List<EventAttendeeLinkProgram> getEventAttendeeLinkPrograms() {
return this.eventAttendeeLinkPrograms;
}
public void setEventAttendeeLinkPrograms(List<EventAttendeeLinkProgram> eventAttendeeLinkPrograms) {
this.eventAttendeeLinkPrograms = eventAttendeeLinkPrograms;
}
public EventAttendeeLinkProgram addEventAttendeeLinkProgram(EventAttendeeLinkProgram eventAttendeeLinkProgram) {
getEventAttendeeLinkPrograms().add(eventAttendeeLinkProgram);
eventAttendeeLinkProgram.setEventAttendee(this);
return eventAttendeeLinkProgram;
}
public EventAttendeeLinkProgram removeEventAttendeeLinkProgram(EventAttendeeLinkProgram eventAttendeeLinkProgram) {
getEventAttendeeLinkPrograms().remove(eventAttendeeLinkProgram);
eventAttendeeLinkProgram.setEventAttendee(null);
return eventAttendeeLinkProgram;
}
}
EventAttendeeLinkProgram.java
#Entity
#Table(name="event_attendee_link_program")
#NamedQuery(name="EventAttendeeLinkProgram.findAll", query="SELECT e FROM EventAttendeeLinkProgram e")
public class EventAttendeeLinkProgram implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private EventAttendeeLinkProgramPK id;
//bi-directional many-to-one association to EventAttendee
#ManyToOne
#JoinColumn(name="attendee_id", insertable=false, updatable=false)
private EventAttendee eventAttendee;
//bi-directional many-to-one association to EventOptionsAttendeeType
#ManyToOne
#JoinColumn(name="attendee_type_id", insertable=false, updatable=false)
private EventOptionsAttendeeType eventOptionsAttendeeType;
//bi-directional many-to-one association to EventProgram
#ManyToOne
#JoinColumn(name="program_id", insertable=false, updatable=false)
private EventProgram eventProgram;
public EventAttendeeLinkProgram() {
}
public EventAttendeeLinkProgramPK getId() {
return this.id;
}
public void setId(EventAttendeeLinkProgramPK id) {
this.id = id;
}
public EventAttendee getEventAttendee() {
return this.eventAttendee;
}
public void setEventAttendee(EventAttendee eventAttendee) {
this.eventAttendee = eventAttendee;
}
public EventOptionsAttendeeType getEventOptionsAttendeeType() {
return this.eventOptionsAttendeeType;
}
public void setEventOptionsAttendeeType(EventOptionsAttendeeType eventOptionsAttendeeType) {
this.eventOptionsAttendeeType = eventOptionsAttendeeType;
}
public EventProgram getEventProgram() {
return this.eventProgram;
}
public void setEventProgram(EventProgram eventProgram) {
this.eventProgram = eventProgram;
}
}
EventAttendeeLinkProgramPK.java
#Embeddable
public class EventAttendeeLinkProgramPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(name="attendee_id", insertable=false, updatable=false)
private int attendeeId;
#Column(name="attendee_type_id", insertable=false, updatable=false)
private int attendeeTypeId;
#Column(name="program_id", insertable=false, updatable=false)
private int programId;
public EventAttendeeLinkProgramPK() {
}
public int getAttendeeId() {
return this.attendeeId;
}
public void setAttendeeId(int attendeeId) {
this.attendeeId = attendeeId;
}
public int getAttendeeTypeId() {
return this.attendeeTypeId;
}
public void setAttendeeTypeId(int attendeeTypeId) {
this.attendeeTypeId = attendeeTypeId;
}
public int getProgramId() {
return this.programId;
}
public void setProgramId(int programId) {
this.programId = programId;
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof EventAttendeeLinkProgramPK)) {
return false;
}
EventAttendeeLinkProgramPK castOther = (EventAttendeeLinkProgramPK)other;
return
(this.attendeeId == castOther.attendeeId)
&& (this.attendeeTypeId == castOther.attendeeTypeId)
&& (this.programId == castOther.programId);
}
public int hashCode() {
final int prime = 31;
int hash = 17;
hash = hash * prime + this.attendeeId;
hash = hash * prime + this.attendeeTypeId;
hash = hash * prime + this.programId;
return hash;
}
}
EventAttendeeServiceImpl.java
#Service
#Primary
public class EventAttendeeServiceImpl implements EventAttendeeService {
#Autowired
private EventAttendeeRepository eventAttendeeRepository;
#Autowired
private EventOptionsAttendeeTypeRepository eventOptionsAttendeeTypeRepository;
#Autowired
private EventProgramRepository eventProgramRepository;
#Override
#Transactional
public String addEventAttendee(EventAttendee eventAttendee) {
EventAttendeeLinkProgram ep = new EventAttendeeLinkProgram();
ep.setEventOptionsAttendeeType(eventOptionsAttendeeTypeRepository.findOne(2L));
ep.setEventProgram(eventProgramRepository.findOne(2L));
eventAttendee.setEventAttendeeLinkPrograms(new ArrayList<>());
eventAttendee.getEventAttendeeLinkPrograms().add(ep);
eventAttendeeRepository.save(eventAttendee);
return "";
}
With this in place, my code is not throwing any errors. It is saving the EventAttendee, but nothing is being saved to the EventAttendeeLinkProgram. Please Note: I am trying so save both EventAttendee and EventAttendeeLinkProgram entities. So I think hibernate should be smart enought to forst save EventAttendee and generating the Id for it, then use that Id to store in EventAttendeeLinkProgram.
Why don't you let spring do the heavy lifting:
First create a JPA repository in spring:
public interface UserRepository extends CrudRepository<User, Long>{
}
Then create your 2 entities with the relationship
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true, fetch = FetchType.EAGER)
private List<UserType> userTypes;
And :
#Entity
public class UserType {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
My test looks like this:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
public class UserRepositoryTest extends AbstractTest {
#Autowired
private UserRepository userRepository;
#Test
#Transactional
public void test1() throws SQLException {
showTables();
User user1 = makeUser("Greg");
userRepository.save(user1);
System.out.println(user1);
userRepository.save(makeUser("George"));
assertEquals(2, userRepository.count());
User user = userRepository.findOne(1l);
}
User makeUser(String name) {
User user = new User();
user.setName(name);
user.setUserTypes(new ArrayList<>());
user.getUserTypes().add(makeUserType("admin"));
user.getUserTypes().add(makeUserType("head chef"));
return user;
}
UserType makeUserType(String description) {
UserType userType = new UserType();
userType.setDescription(description);
return userType;
}
}
First of all, user save return the identifier directly
Long insertId = (Long) session.save(user);
Then you'd better call the rollback on the txtransaction itself instead of retrieving again the transaction from the session.
Finally, when using spring you should consider to let spring manage the transaction itself (container managed transaction)using #Transactional annotation instead of using user managed transaction. It's logical as you let spring manage the session for you (sessionFactory.getCurrentSession()) and both session and transaction should have the same scope (e.g. the unit of work).
Consider reading some literature on Session (e.g. JPA entityManager) and transaction management.

Resources