(This question has a spanish version: Pregunta español StackOverflow)
Hello,
im creating a API-REST with springboot, hibernate ...
In one controller, im returning one entity, the point is, when i do this:
Empresa company= empresaManager.findById(2L);
return company;
returns exactly what i expect, (the object company has a list of students, and has only 2 vinculated).
But when instead of use a number, what i do is get the students, and afterwards, return the company of the students, the company that its returning me comes with 12 students (6 times repeated each student)
String token = request.getHeader("Authorization");
token = token.replace("Bearer ", "");
Usuario usuario = tokenManager.getUsuarioFromToken(token);
Long id = usuario.getEmpresa().getIdempresa();
Empresa empresaOriginal = empresaManager.findById(id);
return empresaOriginal;
Any chance you know why is happenning this ?
This is how should return the object company:
And this is how im actually getting it:
From here, to down, is what is asked in comments
This is my user entity :
#Entity
#Table(name = "usuario")
public class Usuario {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idusuario")
private Long idusuario;
#Column(name = "nombre", length = 30, nullable = false)
private String nombre;
#Column(name = "email", length = 80, nullable = false)
private String email;
#JsonIgnore
#Column(name = "contraseña", length = 300, nullable = false)
private String contraseña;
#JsonIgnore
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "empresa_idempresa"), name = "empresa_idempresa")
private Empresa empresa;
#OneToMany(mappedBy = "usuario", orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Fichaje> fichajes;
public Usuario() {
}
public Empresa getEmpresa() {
return empresa;
}
public void setEmpresa(Empresa empresa) {
this.empresa = empresa;
}
}
This is my Company entity:
#Entity
#Table(name = "empresa")
public class Empresa {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idempresa")
private Long idempresa;
#Column(name = "nombre", length = 100, nullable = false)
private String nombre;
#Column(name = "contacto", length = 300, nullable = false)
private String contacto;
#Column(name = "fecha_inicio_practicas", columnDefinition = "DATE")
private LocalDate inicioPracticas;
#Column(name = "direccion", length = 100)
private String direccion;
#Column(name = "foto_empresa")
private String fotoEmpresa;
#OneToMany(mappedBy = "empresa", orphanRemoval = true, cascade = CascadeType.ALL)
private List<EmpresaTieneDia> empresaTieneDias;
#OneToMany(mappedBy = "empresa", orphanRemoval = false, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Usuario> estudiantes;
public Empresa() {
}
public Long getIdempresa() {
return idempresa;
}
public void setIdempresa(Long idempresa) {
this.idempresa = idempresa;
}
public List<Usuario> getEstudiantes() {
return estudiantes;
}
public void setEstudiantes(List<Usuario> estudiantes) {
this.estudiantes = estudiantes;
}
}
This is the findById:
(Service or Manager)
public Empresa findById(Long id) {
return this.empresaRepository.findByIdempresa(id);
}
(Repository or DAO)
public interface EmpresaRepository extends CrudRepository<Empresa, Long> {
Empresa findByIdempresa(Long id);
}
SOLVED
I had to change my listo of users from a List<Usuario> to a Set<Usuario>.
Anyways, I understand why it fixes it using a Set (cause doesn't let duplicated values in it).
Anyways, if someone could try to explain my why in the first moment I'm having 6 times duplicated every item in the list i would apreciate it.
Thanks !! :D
Related
I have a Production class and ProductionDetail entity class where Id of Production table is a foreignKey as production_id in ProductionDetail entity class so my both entity class with mapping has given bellow
Production Entity Class:
#Entity
#Table(name = "tbl_production")
#XmlRootElement
public class TblProduction implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 45)
#Column(name = "ID")
private String id;
#Column(name = "PRODUCTION_DATE")
#Temporal(TemporalType.DATE)
private Date productionDate;
#Column(name = "START_DATETIME")
#Temporal(TemporalType.TIMESTAMP)
private Date startDatetime;
#Column(name = "END_DATETIME")
#Temporal(TemporalType.TIMESTAMP)
private Date endDatetime;
#Size(max = 45)
#Column(name = "MACHINE_UUID")
private String machineUuid;
**Relation with Production Details Table**
#OneToMany(mappedBy = "production")
#XmlElement(name = "productionDetails")
private List<TblProductionDetail> productionDetailList;
#PrimaryKeyJoinColumn(name = "MACHINE_UUID", referencedColumnName = "UUID")
#ManyToOne(fetch = FetchType.LAZY)
private MstMachine mstMachine;
#XmlTransient
public MstMachine getMstMachine() {
return this.mstMachine;
}
}
Production Details Entity Class:
#Entity
#Table(name = "tbl_production_detail")
#XmlRootElement
public class TblProductionDetail implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 45)
#Column(name = "ID")
private String id;
#Size(max = 45)
#Column(name = "COMPONENT_ID")
private String componentId;
#Size(max = 45)
#Column(name = "PRODUCTION_ID")
private String productionId;
**Relation with Production Class**
#ManyToOne
#JoinColumn(name = "PRODUCTION_ID", referencedColumnName = "ID", insertable = false,
updatable = false)
private TblProduction production;
#Transient
public String componentCode;
#Transient
public String componentName;
#PrimaryKeyJoinColumn(name = "COMPONENT_ID", referencedColumnName = "ID")
#ManyToOne(fetch = FetchType.LAZY)
private MstComponent mstComponent;
#XmlTransient
public MstComponent getMstComponent() {
return this.mstComponent;
}
public void setMstComponent(MstComponent mstComponent) {
this.mstComponent = mstComponent;
}
}
ParentList Class:
public class TblProductionList {
private List<TblProduction> productionList;
public TblProductionList() {
productionList = new ArrayList<>();
}
public List<TblProduction> getTblProductions() {
return productionList;
}
public void setTblProductions(List<TblProduction> tblProductionList) {
this.productionList = tblProductionList;
}
}
BusinessLogic(DAO Class):
public TblProductionList getJson() {
TblProductionList response = new TblProductionList();
StringBuilder retrieveQuery = new StringBuilder();
retrieveQuery.append(" SELECT prod FROM TblProduction prod ");
retrieveQuery.append(" JOIN FETCH prod.productionDetailList ");
retrieveQuery.append(" WHERE prod.endDatetime IS NULL ");
retrieveQuery.append(" AND prod.machineUuid IS NOT NULL ");
retrieveQuery.append(" AND NOT EXISTS (SELECT tpt FROM
TblProductionThset tpt WHERE prod.id = tpt.productionId) ");
retrieveQuery.append(" AND EXISTS (SELECT mmfd FROM
MstMachineFileDef mmfd WHERE prod.machineUuid = mmfd.machineUuid
AND mmfd.hasThreshold = 1) ");
retrieveQuery.append(" ORDER BY prod.id ");
Query query =
entityManager.createQuery(retrieveQuery.toString());
List thresholdList = query.getResultList();
response.setTblProductions(thresholdList);
return response;
}
According to the database I am getting expected master child data like below
After designing this entity class I am expecting that I will get 3 master records where each record has 2 detail records. But I am getting 6 duplicate master records with 12 child records. Can anyone suggest to me please where is my code became wrong and why this situation raised? please check the JSON data that I am getting from API.
change your array list to hash set then records are not duplicate.
I'm using JPA with QueryDSL, In that scenario, I'm going to use JpaQuery Projections for fetching the only required data from DB. In my entity there is one OneToMany(List) Mapping But I'm not able to do projection on OneToMany mapping fields ie List. Please, anyone, tell me how we can get List by using projections in OneToMany mapping.
My entity class:-
public class Brand {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = COLUMN_BRAND_ID)
private int brandId;
#Column(name = COLUMN_BRAND_NAME, columnDefinition = "VARCHAR(255)", nullable = false, unique = true)
private String brandName;
#OneToOne(cascade = CascadeType.MERGE )
#JoinColumn(name = BRAND_LOGO_SMALL_FOREIGN_KEY)
private File brandLogoSmall;
#OneToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = BRAND_LOGO_LARGE_FOREIGN_KEY)
private File brandLogoLarge;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = COLUMN_CONTACT_PERSONS, nullable = true)
private List<BrandContactPerson> contactPersons;
#Column(name = COLUMN_OTHER_DETAILS, columnDefinition = "VARCHAR(255)", nullable = true)
private String otherDetails;
#Column(name = COLUMN_BRAND_STATUS, columnDefinition = "TINYINT(1) DEFAULT 1", nullable = false)
private int status;
#Column(name = BRAND_ADDED_BY_FOREIGN_KEY, nullable = true)
private int createdBy;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = COLUMN_BRAND_ADDED_ON, columnDefinition = "DATETIME", nullable = false)
private Date createdDate;
#Column(name = BRAND_MODIFIED_BY_FOREIGN_KEY, nullable = true)
private int updatedBy;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = COLUMN_BRAND_MODIFIED_ON, columnDefinition = "DATETIME", nullable = true)
private Date updatedDate;
#OneToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = COLUMN_CLASSIFICATION)
private Classification classification;
//Setter Getter
}
Service Impl Class
#Repository
public class BrandCustomRepositoryImpl implements BrandCustomRepository {
#Autowired
private BrandQueryDslRepository brandQueryDslRepository;
#Autowired
private EntityManager em;
#Override
public List<Brand> search(BrandSearchModel searchQuery) {
JPAQuery<Contract> query = new JPAQuery<>(em);
QBrand qBrand = QBrand.brand;
BooleanBuilder builder = new BooleanBuilder();
if (searchQuery.getPageNo() == null) {
searchQuery.setPageNo(0);
}
if (searchQuery.getPageSize() == null) {
searchQuery.setPageSize(UserSearchModel.DEFAULT_PAGE_SIZE);
}
prepareBrandSearchBuilder(builder, qBrand, searchQuery);
builder.and(qBrand.status.eq(Constant.ACTIVE));
List<Brand> brand1 = query.from(qBrand.brand).where(builder).offset(0).limit(20)
.select(Projections.bean(Brand.class, qBrand.brandName,qBrand.contactPersons))
.fetch();
return brand1;
}
This is the above example. Thanks in advance.
I am new to Hibernate. I have two tables Team(parent) and Product(child) with TEAM_ID column as a relationship, each team will have multiple products and each product will have single team. I have created entity classes with #OneToMany mapping in Team class and #ManyToOne in Product class.
I need to coverup below scenarios,
To save both product and team when team is new
Save only product if the team is already available
When i am trying to save product it tries to save team again throws constraint error.
Please help.
Team:
#Entity
#Table(name = "TEAM")
public class Team implements Serializable{
private static final long serialVersionUID = 5819170381583611288L;
#Id
#SequenceGenerator(name="teamIdSeq",sequenceName="team_id_seq",allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="teamIdSeq")
#Column(name="TEAM_ID", updatable = false, nullable = false, unique = true)
private int teamId;
#Column(name="NAME", nullable = false, unique = true)
private String teamName;
#Column(name="DESCRIPTION", nullable = false)
private String teamDesc;
#Column(name="CONTACTS", nullable = false)
private String contacts;
#Column(name="APPROVER_NAME", nullable = false)
private String approverName;
#Column(name="APPROVAL_STATUS", nullable = false)
private int approvalStatus;
#Temporal(TemporalType.DATE)
#Column(name="CREATED_ON", nullable = false)
private Date createdOn;
#Column(name="CREATED_BY", nullable = false)
private String createdBy;
#Temporal(TemporalType.DATE)
#Column(name="MODIFIED_ON", nullable = false)
private Date modifiedOn;
#Column(name="MODIFIED_BY", nullable = false)
private String modifiedBy;
#OneToMany(fetch = FetchType.LAZY, mappedBy="team", cascade = CascadeType.ALL)
private Set<Product> products;
//setters and getters
}
Product:
#Entity
#Table(name = "PRODUCT", uniqueConstraints = {#UniqueConstraint(columnNames = {"PRODUCT_ID", "TEAM_ID"})})
public class Product implements Serializable{
private static final long serialVersionUID = 5819170381583611288L;
#Id
#SequenceGenerator(name="productIdSeq", sequenceName="product_id_seq",allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="productIdSeq")
#Column(name="PRODUCT_ID", updatable = false, nullable = false)
private int productId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "TEAM_ID")
private Team team;
#Column(name="NAME", nullable = false, unique = true)
private String productName;
#Column(name="DESCRIPTION", nullable = true)
private String productDesc;
#Column(name="APPROVER_NAME", nullable = false)
private String approverName;
#Column(name="APPROVAL_STATUS", nullable = false)
private int approvalStatus;
#Temporal(TemporalType.DATE)
#Column(name="CREATED_ON", nullable = false)
private Date createdOn;
#Column(name="CREATED_BY", nullable = false)
private String createdBy;
#Temporal(TemporalType.DATE)
#Column(name="MODIFIED_ON", nullable = false)
private Date modifiedOn;
#Column(name="MODIFIED_BY", nullable = false)
private String modifiedBy;
#OneToMany(fetch = FetchType.LAZY, mappedBy="product")
private Set<Form> forms;
//setters and getters
}
DAO:
#Repository
#EnableTransactionManagement
public class KMDBDAOImpl implements KMDBDAO {
#Autowired
private SessionFactory sessionFactory;
public void addTeam(Team team) {
Product product = new Product(team, "BMA" + Math.random(), "UI Tool", "test",
1, new Date(), "test", new Date(), "test");
Set<Product> products = new HashSet<Product>();
products.add(product);
team.setProducts(products);
if(getTeam(team.getTeamName()) != null) {
product.setTeam(getTeam(team.getTeamName()));
sessionFactory.getCurrentSession().saveOrUpdate(product);
} else {
sessionFactory.getCurrentSession().saveOrUpdate(team);
}
}
public Team getTeam(String teamName) {
Query query = sessionFactory.getCurrentSession().createQuery("from Team where teamName = :name");
query.setString("name", "teamName");
return (query.list().size() > 0 ? (Team) query.list().get(0) : null);
}
The only time that you should set Product list on Team is when Team is a new entity. So:
Set<Product> products = new HashSet<Product>();
products.add(product);
if(getTeam(team.getTeamName()) != null) {
product.setTeam(getTeam(team.getTeamName()));
sessionFactory.getCurrentSession().saveOrUpdate(product);
} else {
team.setProducts(products);
sessionFactory.getCurrentSession().saveOrUpdate(team);
}
i give you some example code for one to many relationship please go through it and let me kn if some problem .... i have 2 tables 1.product 2.sku my condition is , one product have many sku's ...
Product.java
#LazyCollection(LazyCollectionOption.FALSE)
#ElementCollection(targetClass=Product.class)
#OneToMany(mappedBy="product" , cascade=CascadeType.MERGE)
private List<Sku> listSkuOrders = new ArrayList<>();
Sku.java
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = PRODUCT_ID , nullable = false)
private Product product;
I'm developing a project which uses BackboneJS in front-end and Java - Spring Core in back-end. I have a problem about mapping entity(domain) objects to DTO objects. I am getting an error message like that :
org.apache.cxf.interceptor.Fault: Infinite recursion (StackOverflowError) (through reference chain: com.countdown.dto.CategoryDTO["countdownList"]->java.util.ArrayList[0]->com.countdown.dto.CountdownDTO["category"]->.......
User.java
#Entity
#Table(name = "Users")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "USER_ID", nullable = false)
private int id;
#Column(name = "EMAIL", nullable = false, unique = true)
private String email;
#Column(name = "NAME_SURNAME", nullable = false)
private String nameSurname;
#Column(name = "PASSWORD", nullable = false)
private String password;
#Column(name = "USERNAME", nullable = false, unique = true)
private String username;
#Column(name = "REGISTER_DATE", nullable = false)
private Date registerDate;
#ManyToOne
#JoinColumn(name = "ROLE_ID")
private Role role;
#OneToMany(mappedBy = "createUser")
private List<Countdown> createCountdownList = new ArrayList<Countdown>();
#OneToMany(mappedBy = "updateUser")
private List<Countdown> updateCountdownList = new ArrayList<Countdown>();
#ManyToMany
#JoinTable(name = "FOLLOWINGS",
joinColumns = #JoinColumn(name = "USER_ID"),
inverseJoinColumns = #JoinColumn(name = "COUNTDOWN_ID"))
private List<Countdown> followings = new ArrayList<Countdown>();
//Getters and setters..
}
Role.java
#Entity
public class Role implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ROLE_ID")
private int id;
#Column(name = "ROLE_NAME", nullable = false)
private String roleName;
#OneToMany(mappedBy = "role",fetch = FetchType.LAZY)
List<User> userList = new ArrayList<User>();
}
Countdown.java
#Entity
#Table(name = "COUNTDOWN")
public class Countdown implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "COUNTDOWN_ID")
private int id;
#Column(name = "COUNTDOWN_NAME", nullable = false)
private String countdownName;
#Column(name = "COUNTDOWN_DATE", nullable = false)
private Date countdownDate;
#Column(columnDefinition = "varchar(5000)")
private String countdownDescription;
#JoinColumn(name = "CATEGORY_ID", nullable = false)
#ManyToOne
private Category category;
#JoinColumn(name = "CREATE_USER", nullable = false)
#ManyToOne
private User createUser;
#Column(name = "CREATE_DATE", nullable = false)
private Date createDate;
#JoinColumn(name = "UPDATE_USER", nullable = false)
#ManyToOne
private User updateUser;
#Column(name = "UPDATE_DATE", nullable = false)
private Date updateDate;
#Column(name = "CREATE_USER_IP", nullable = false)
private int createIP;
#ManyToMany
private List<User> followers = new ArrayList<User>();
}
Category.java
#Entity
#Table(name="CATEGORY")
public class Category implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="CATEGORY_ID")
private int id;
#Column(name = "CATEGORY_NAME" , nullable = false)
private String categoryName;
#OneToMany(mappedBy = "category")
private List<Countdown> countdownList = new ArrayList<Countdown>();
}
Business Logic : CategoryServiceImpl.java
I'm getting error in forEach loop.
#Transactional(readOnly = true)
public List<CategoryDTO> getAllCategories() {
List<Category> categoryList;
List<CategoryDTO> categoryDTOList = new ArrayList<CategoryDTO>();
logger.debug("getAllCategories called");
try {
categoryList = categoryDAO.findAll();
for(Category category : categoryList){
categoryDTOList.add(mapper.map(category,CategoryDTO.class));
}
}catch (NoResultException e){
logger.error("getAllCategories method : No Category wasn't found");
logger.warn(e,e);
}catch (Exception e){
logger.error("getAllCategories method : Categories wasn't found");
logger.warn(e,e);
}
return categoryDTOList;
}
Also Do I have to use DTO object in Presentation Layer? Can I use entity objects in presentation layer instead of DTO objects?
How can I solve this problem? Sorry my bad english. Thank you!
Please Try :
#Transactional(readOnly = true)
public List<CategoryDTO> getAllCategories() {
List<Category> categoryList;
List<CategoryDTO> categoryDTOList = new ArrayList<CategoryDTO>();
logger.debug("getAllCategories called");
try {
categoryList = categoryDAO.findAll();
for(Category category : categoryList){
if(category.getCountdownList() != null && !category.getCountdownList().isEmpty()){
for(Countdown countdown : category.getCountdownList()){
countdown.setCategory(null);
}
}
categoryDTOList.add(mapper.map(category,CategoryDTO.class));
}
}catch (NoResultException e){
logger.error("getAllCategories method : Hata: No Category wasn't found");
logger.warn(e,e);
}catch (Exception e){
logger.error("getAllCategories method : Hata: Categories wasn't found");
logger.warn(e,e);
}
return categoryDTOList;
}
For those who are struggling with infinite recursion issue in Dozer.
I use mapId to define a leaf object and stops the recursion.
Let assume we have two entities Course and Teacher, which contains a Many-to-Many relationship, and we want to convert the following object graph to one represented by CourseDTO and TeacherDto. And we hope Dozer stops at the 3rd level.
Teacher 1 ---> m Course 1 ---> m Teacher ---> ...
1st level 2nd level 3rd level
We can first define the following definition for Teacher to TeacherDTO conversion.
This first mapping is used for the root Teacher entity.
Include any other fields you need in the mapping.
mapping(Teacher.class, TeacherDTO.class,
TypeMappingOptions.oneWay()
, mapNull(false)
).fields("courses", "courses");
The following mapping will prevent Dozer from going further to map the contained Course. We define a mapId teacherLeaf for it.
Exclude the fields that cause the infinite recursion. (In my example, it's courses)
Include any other fields you need in the mapping.
mapping(Teacher.class, TeacherDTO.class,
TypeMappingOptions.oneWay(), TypeMappingOptions.mapId("teacherLeaf")
, mapNull(false)
).exclude("courses");
The last one is the mapping rule for Course to courseDTO. The key is that we tell the mapper to use the teacherLeaf mapping rule defined previously to convert the contained Teachers.
mapping(Course.class, CourseDTO.class,
TypeMappingOptions.oneWay()
, mapNull(false)
).fields("teachers", "teachers", useMapId("teacherLeaf"));
Hope this helps!
I use Dozer 6.1.0.
I'm using hibernate JPA annotations, and as for repositories I'm using Spring JPA
i have the following two entities:
#Entity
#Table(name = "idn_organization")
#Audited
public class Organization extends UuidBasedEntity {
#Column(name = "full_name", length = 64, nullable = false)
private String fullName;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinTable(
name = "idn_organization_address",
joinColumns = #JoinColumn(name = "organization_id"),
inverseJoinColumns = #JoinColumn(name = "address_id")
)
private Set<Address> addresses;
#Column(name = "phone_number", length = 12, nullable = true)
private String phoneNo;
#Column(name = "registered_date", nullable = true)
private Date registeredDate;
#Column(name = "social_security", length = 9, nullable = true)
private String socialSecurity;
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public Set<Address> getAddresses() {
return addresses != null ? addresses : new HashSet<Address>();
}
public void setAddresses(Set<Address> addresses) {
if (this.addresses == null)
this.addresses = new HashSet<>();
this.addresses.clear();
if (addresses != null)
this.addresses.addAll(addresses);
}
And:
#Entity
#Table(name = "cmn_address")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
#Audited
public class Address extends AutoIdBasedEntity {
#Valid
#NotNull(message = "{address.type.notNull}")
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "type_code")
#ForeignKey(name = "FK_address_address_type")
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
private AddressType type;
#NotNull(message = "{address.line1.notNull}")
#Column(name = "address_line_1", length = 256, nullable = true)
private String addressLine1;
#Column(name = "address_line_2", length = 128, nullable = true)
private String addressLine2;
#Column(name = "address_line_3", length = 128, nullable = true)
private String addressLine3;
#Valid
#NotNull(message = "{address.town.notNull}")
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "city_id")
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
private Town city;
#NotNull(message = "{address.state.notNull}")
#Size(min = 2, max = 2, message = "{address.state.notEmpty}")
#Column(name = "state_code")
private String state;
//#NotNull(message = "{address.zip.notNull}")
// TODO Not all DTOP Results match
// #Pattern(regexp = "\\d{5}(-\\d{4})?", message = "{address.zip.notValid}")
#Column(name = "postal_code", length = 32, nullable = true)
private String postalCode;
#Valid
#NotNull(message = "{address.country.notNull}")
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "country_code")
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
private Country country;
As mentioned my repository is:
public interface OrganizationRepository extends JpaRepository<Organization, String>, JpaSpecificationExecutor<Organization> {
}
The problem I'm having is specifically with the method save in my service:
#Service("identityService")
#Transactional(readOnly = true, propagation = Propagation.REQUIRED)
public class IdentityServiceImpl implements IdentityService {
private static Logger logger = LoggerFactory.getLogger(IdentityServiceImpl.class);
#Autowired
private OrganizationRepository organizationRepository;
#Override
public void persistOrganization(Organization organization) {
organizationRepository.save(organization);
if (logger.isDebugEnabled()) {
logger.debug("Organization [" +
organization.getUuid() + " - " +
organization.getFullName() + "] created or updated.");
}
}
}
Whenever I call my save method the organization is persisted and so is the address in their respective tables... but the join table entry is not! Therefore, I obtain a new organization, a new address, but there's no link between them. So what am I doing wrong here? It obviously also happens when I try to edit the organization somehow.
The problem was in my service as I had the annotation:
#Transactional(readOnly = true, propagation = Propagation.REQUIRED)
and because I was managing transactions through web flow, they never commited. So I added #Transactional to the persist methods and that fixed it. You can also just remove the readOnly = true part, but I didn't because there were fetch methods that I didn't want to be exposed.