How to lazy load many-to-one column with #LastModifiedBy annotation?
For example I have createdBy and lastModifiedBy columns:
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "manager_id")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
#JsonIdentityReference(alwaysAsId = true)
private User manager;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#CreatedBy
#JoinColumn(name = "created_by")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
#JsonIdentityReference(alwaysAsId = true)
#JsonProperty("created_user_id")
private User createdBy;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#LastModifiedBy
#JoinColumn(name = "last_modified_by")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
#JsonIdentityReference(alwaysAsId = true)
#JsonProperty("last_modified_user_id")
private User lastModifiedBy;
It's work. But as soon as I try to lazy load column lastModifiedBy (fetch = FetchType.LAZY, optional = false) I have error:
2016-06-18 23:10:36,399 WARN [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] (default task-8) Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Problem accessing property 'id': null (through reference chain: com.smcontact.cms.model.project.Project["manager"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Problem accessing property 'id': null (through reference chain: com.smcontact.cms.model.project.Project["manager"])
How to fix this error?
Solution: register hibernate module.
Hibernate5Module hibernateModule = new Hibernate5Module();
objectMapper.registerModule(hibernateModule);
Related
I have the following Entities in my Project:
#Getter
#Setter
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = { "purchaseId" }))
public class Purchase {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long purchaseId;
#Column(unique = true, nullable = false, length = 15)
private String purchaseNo;
#Column(nullable = false, length = 15)
private String batchCode;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "supplier.supplierId", foreignKey = #ForeignKey(name = "FK_purchase_supplier"), nullable = false)
private Supplier supplier;
#Column(nullable = false)
private LocalDate purchaseDate;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "purchaseId", nullable = false)
private List<PurchaseItem> purchaseItems;
private Double totalAmount;
#ManyToOne
#JoinColumn(name = "userId", nullable = false, foreignKey = #ForeignKey(name = "FK_invoice_purchases"))
private User staff;
#Column(length = 100)
private String remarks;
#Column(nullable = false, updatable = false)
#CreationTimestamp
private LocalDateTime createdAt;
private boolean isDeleted = false;
}
#Getter
#Setter
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = {"purchaseItemId"}))
public class PurchaseItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long purchaseItemId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "purchaseId", insertable = false, updatable = false, foreignKey = #ForeignKey(name="FK_purchase_item"))
private Purchase purchase;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "productId", foreignKey = #ForeignKey(name="FK_product_item"), nullable = false)
private Product product;
private Double itemAmount;
#Column(nullable = false)
private Double quantity;
private Double itemTotalAmount;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#PrimaryKeyJoinColumn(foreignKey = #ForeignKey(name = "FK_purchacase_item_batch"))
private PurchaseProductBatch productPurchaseBatch;
public void setPurchaseProductBatch() {
PurchaseProductBatch productPurchaseBatch = new PurchaseProductBatch();
productPurchaseBatch.setProduct(this.product);
productPurchaseBatch.setQuantity(this.quantity);
productPurchaseBatch.setPurchaseItem(this);
this.productPurchaseBatch = productPurchaseBatch;
}
}
#Getter
#Setter
#Entity
#Table()
public class PurchaseProductBatch{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long productBatchId;
#ManyToOne(cascade = CascadeType.DETACH)
#JoinColumn(name = "productId", foreignKey = #ForeignKey(name = "FK_product_purch"))
private Product product;
private Double quantity;
#OneToOne(fetch = FetchType.EAGER)
#MapsId
private PurchaseItem purchaseItem;
private boolean isDeleted = false;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "productBatchId", foreignKey = #ForeignKey(name = "FK_purchase_batch_qty"))
private Set<InvoicePurchaseBatchQuantity> invoicePurchaseBatchQuantities;
}
During Purchase Insert, everything works fine. However, if I update the Purchase record in the database and add new PurchaseItem entry, I encounter the issue below:
org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value : com.be.entity.PurchaseItem.product; nested
I have debugged my application and I see that there is a Product instance inside all of the PurchaseItem. When I commented out the PurchaseProductBatch inside PurchaseItem, everything works fine so I conclude that it is the causing the issue. However, I don't understand how and why JPA seems to create phantom PurchaseItem Records with no value.
Also, if I only update an existing PurchaseItem entry in Purchase, I don't encounter any issues.
I have the following entity and I'm trying to use ExampleMatcher for simple queries:
#Entity(name="UserAccount")
#Table(name = "useraccount", catalog = "useraccount")
#Data
public class UserAccount implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false, unique = true)
private String username;
#Column(nullable = false, unique = true)
private String mail;
private String password;
private boolean isEnabled;
private Timestamp credentialExpire;
private boolean isAccountNonLocked;
private boolean isSuspended;
private Timestamp accountExpire;
#ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
#JoinTable(name = "user_to_privileges", catalog = "useraccount",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "privilege_id", referencedColumnName = "id", nullable = false)})
private Set<Privilege> privileges= new HashSet<>();
#ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
#JoinTable(name = "user_to_organizations", catalog = "useraccount",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "organization_id", referencedColumnName = "id", nullable = false)})
private Set<Organization> organizations= new HashSet<>();
#OneToOne(mappedBy ="user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
#PrimaryKeyJoinColumn
#Setter(AccessLevel.NONE)
private UserRegister register;
#OneToMany(mappedBy ="tokenId.user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
private Set<SecureToken> tokens = new HashSet<>();
#OneToOne(mappedBy ="user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
#PrimaryKeyJoinColumn
private UserLogin login;
//all the methods omitted from brevity
}
I create the Example matcher as follows:
UserAccount account= new UserAccount();
account.setUsername("John");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnoreCase()
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);
Example<UserAccount> regExample = Example.of(account, matcher);
List<UserAccount> out = repository.findAll(regExample);
Consider that a user with username "John" exists, but the output is always empty, no matter what parameter I fill.
Edit: this helps to find the solution: Are there any possible ways to ignore all paths of JPA Example Matcher. There is no way to automatically ignore primitive fields when not used?
Edit: Notice that I want to find all the UserAccount containing the specified strings in the selected fields. With other entities the configuration of ExampleMatcher works.
I have a project that is split between multiple modules, each module is imported into the main one as a maven dependency.
Persistence entities can be located at any of the projects but under the same package.
I have been trying to improve the startup time of the application by using the spring-context-indexer but it seems to cause an issue with detecting entities.
My #EntityScan is configured like this:
#EntityScan(basePackages = {"com.botscrew", "com.botscrew.demoadminpanel.entity.jpa","com.botscrew.admin.entity"})
The strange thing is that error looks like this
org.hibernate.AnnotationException: #OneToOne or #ManyToOne on com.botscrew.admin.entity.Bot.amioWhatsAppConfigs references an unknown entity: com.botscrew.admin.entity.services.configs.AmioWhatsAppConfigs
Essentially both entities are located under the same package but Bot entity was resolved but AmioWhatsAppConfigs was not.
The application starts perfectly fine without spring indexer.
I am using spring boot 2.2.1.RELEASE
Entities classes:
#Getter
#Setter
#Builder
#Entity
#ToString(of = {"id", "name"})
#AllArgsConstructor
#Table(name = "admin_bot")
#DiscriminatorValue("Bot")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Bot {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Convert(converter = EmojiConverter.class)
private String name;
private Integer timezone;
private String greetingText;
#Column(columnDefinition = "tinyint(1) default 1")
private Boolean active;
#Column(unique = true, updatable = false, nullable = false)
private String publicIdentifier;
#OneToOne(fetch = FetchType.LAZY)
private PersistentMenuEntity persistentMenuEntity;
//TODO FetchType.LAZY
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "widget_id")
private Widget widget;
//TODO FetchType.LAZY
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private MessengerConfigs messengerConfigs;
//TODO FetchType.LAZY
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private AmioWhatsAppConfigs amioWhatsAppConfigs;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private TwilioConfigs twilioConfigs;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private DialogflowConfigs dialogflowConfigs;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private ChatbaseConfig chatbaseConfig;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private SupportSettings supportSettings;
#OneToMany
private Set<Tag> tags;
#OneToMany(mappedBy = "bot")
private List<Broadcast> broadcasts;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "admin_bot_features",
joinColumns = {#JoinColumn(name = "bot_id")},
inverseJoinColumns = {#JoinColumn(name = "feature_id")})
private Set<Feature> features;
public Bot() {
this.active = true;
}
public Bot(String name, DefaultWidgetProperties defaultWidgetProperties) {
this.publicIdentifier = UUID.randomUUID().toString();
this.chatbaseConfig = new ChatbaseConfig();
this.amioWhatsAppConfigs = new AmioWhatsAppConfigs();
this.timezone = 0;
this.name = name;
this.active = true;
this.messengerConfigs = new MessengerConfigs();
this.dialogflowConfigs = new DialogflowConfigs();
this.widget = new Widget(defaultWidgetProperties);
this.supportSettings = new SupportSettings(false);
}
}
#Getter
#Setter
#Entity
#Accessors(chain = true)
#ToString
#Table(name = "admin_amio_whatsapp_configs")
public class AmioWhatsAppConfigs implements AmioWhatsAppBot {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String channelId;
private String accessToken;
private String secret;
}
Please help
I am editing my answer please check example
#EntityScan(basePackages = {"com.botscrew",
"com.botscrew.demoadminpanel.entity.jpa","com.botscrew.admin.entity.services.configs.*"})
Here is the code:
#Entity
public class PortalUser {
#NotNull
#OneToMany(mappedBy = "portalUser", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<PortalUserOrganisation> portalUserOrganisations;
#NotNull
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "portalUser", orphanRemoval = true)
private Set<UserRole> userRoles = new HashSet<UserRole>();
}
#Entity
public class PortalUserOrganisation {
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private PortalUser portalUser;
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ORGANISATION_ID", referencedColumnName = "ID")
private Organisation organisation;
}
#Entity
public class Organisation {
#OneToMany(mappedBy = "organisation", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
private Set<PortalUserOrganisation> portalUserOrganisations;
}
#Entity
public class UserRole {
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private PortalUser portalUser;
#NotNull
#ManyToOne(fetch = FetchType.LAZY, optional=true)
#JoinColumn(name = "ROLE_ID", referencedColumnName = "ID")
private RoleLookup roleLookup;
}
#Entity
public class RoleLookup extends AbstractLookupEntity {
#OneToMany(mappedBy = "roleLookup", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
private Set<UserRole> userRoles = new HashSet<UserRole>();
}
Code to Create a User:
#Transactional
saveUser(userObj)
PortalUser portalUser = new PortalUser;
portalUser.setStatus(status);
PortalUserOrganisation userOrganisation = null;
for (OrganisationsDto dto : organisationsList()) {
userOrganisation = new PortalUserOrganisation();
userOrganisation.setOrganisation(organisationRepository.findOne(dto.getId()));
userOrganisation.setPortalUser(portalUser);
userOrganisation.setCreatedUpdatedBy(context.getName());
userOrganisation.setCreatedUpdatedDate(createUpdateDate);
userOrganisation.setAction(portalUser.getAction());
userOrganisation.setStatus(portalUser.getStatus());
userOrganisation.setActive(true);
portalUser.getPortalUserOrganisation().add(userOrganisation);
}
UserRole userRole = null;
for (RoleLookupDto dto : portalUserDto.getUserRoles()) {
userRole = new UserRole();
userRole.setPortalUser(portalUser);
userRole.setRoleLookup(roleLookupRepository.findOne(dto.getId()));
userRole.setCreatedUpdatedBy(context.getName());
userRole.setCreatedUpdatedDate(createUpdateDate);
userRole.setAction(portalUser.getAction());
userRole.setStatus(portalUser.getStatus());
userRole.setActive(true);
portalUser.getUserRole().add(userRole);
}
portalUser.setActive(false);
portalUser = portalUserRepository.save(portalUser);
return portalUser;
I have see so many post, but this has not solved my issue. Any help is appreciated. Here the RoleLookup is a static table. Here is the exception:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.commerzbank.clearing.ccp.domain.UserRole.roleLookup -> com.commerzbank.clearing.ccp.domain.RoleLookup; nested exception is java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.commerzbank.clearing.ccp.domain.UserRole.roleLookup -> com.commerzbank.clearing.ccp.domain.RoleLookup
You should set a cascade = "save-update " for many-to-one side.
I have 1 entity call Item in which I want to be able to link parent items to children. to use a join table to create a parent/child relationship. I haven't been able to get any good documentation on. So if anyone has any thoughts I'm all ears.
Here is what I have... which works most of the time.
public class Item implements java.io.Serializable {
#Id
private Long id;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinTable(name = "ITEMTOITEM", joinColumns = { #JoinColumn(name = "ITEMID") }, inverseJoinColumns = { #JoinColumn(name = "PARENTITEMID") } )
private Item parent;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<Item> children;
}
At times when I want to bring back objects that are tied to this item table I am getting an error of the following:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.webflow.engine.ActionExecutionException: Exception thrown executing [AnnotatedAction#6669ff5 targetAction = com.assisted.movein.web.common.nav.NavAction#6edf74b7, attributes = map['method' -> 'handleEntry']] in state 'oneTimeChargesAndFeesView' of flow 'in-flow' -- action execution attributes were 'map['method' -> 'handleEntry']'; nested exception is Exception [TOPLINK-4002] (Oracle TopLink Essentials - 2.0.1 (Build b04-fcs (04/11/2008))): oracle.toplink.essentials.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: ORA-00904: "PARENTITEM_ITEMID": invalid identifier
Error Code: 904
Call: SELECT ITEMID, ITEMSHORTDESC, EFFENDDATE, ITEMDESC, PARENTITEM_ITEMID, ITEMTYPECODE FROM ITEM WHERE (ITEMID = ?)
bind => [1250]
Query: ReadObjectQuery(com.domain.Item)
Any help would be appreciated.
Try to use #JoinColumninstead:
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "PARENTITEMID", referencedColumnName = "ITEMID")
private Item parent;
#OneToMany(
cascade = {CascadeType.ALL},
orphanRemoval = true,
fetch = FetchType.LAZY
)
#JoinColumn(name = "PARENTITEMID")
private List<Item> children;
After a lot of reading on JPA 2.0 I figured out that I needed a newer version of Eclipselink 2.3+ in order to support what I was trying to do. Here is the code that actually worked in my case, but it will not work due to our dependency on an older version [EclipseLink 2.0.2]. Also another project's is still using Toplink-essentials JPA 1.0 which again does like this notation.
public class Item {
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinTable(name = "ITEMINCENTIVESMAPPING",
joinColumns = { #JoinColumn(name = "INCENTIVEITEMID", referencedColumnName = "ITEMID", insertable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "ITEMID", referencedColumnName = "ITEMID", insertable = false, updatable = false) } )
private Item parentItem;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "ITEMINCENTIVESMAPPING",
joinColumns = { #JoinColumn(name = "INCENTIVEITEMID", referencedColumnName = "ITEMID") },
inverseJoinColumns = { #JoinColumn(name = "ITEMID", referencedColumnName = "ITEMID") } )
private List<Item> items;
}
I believe #JoinTable is only allowed on a #OneToOne or #OneToMany or #ManyToMany, I don't think it makes any sense on a #ManyToOne. Use a #JoinColumn, or a #OneToOne.
Also your #OneToMany does not make sense, the mappedBy must be an attribute and you have no parentItem, it should be parent, and parent should use a #JoinColumn.