How to fetch data from multiple table query by hibernate? - java

I have a user management application that allocate each user a team and one or many access to different application. Now for the reporting page I am trying to fetch data from two table (UserInfo & UserAppAccess) by Hibernate but I can't.
Here are the tables :
Table 1 (UserInfo):
#Entity
#Table(name = "user_info", uniqueConstraints = { #UniqueConstraint(columnNames = "username"), #UniqueConstraint(columnNames = "email") })
public class UserInfo implements java.io.Serializable {
public enum UserStatus {
active, inactive
}
public enum UserType {
user, creator
}
private static final long serialVersionUID = 2650114334774359089L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 100)
private String id;
#Column(name = "username", unique = true, nullable = false, length = 50)
private String username;
#Column(name = "password", nullable = false, length = 80)
private String password;
#Column(name = "status", nullable = false, length = 10)
#Enumerated(EnumType.STRING)
private UserStatus status;
#Column(name = "type", nullable = false, length = 10)
#Enumerated(EnumType.STRING)
private UserType type;
#Column(name = "phone", nullable = true, length = 30)
private String phone;
#Column(name = "email", nullable = true, length = 50)
private String email;
#Column(name = "first_name", nullable = true, length = 50)
private String firstName;
#Column(name = "last_name", nullable = true, length = 50)
private String lastName;
#Column(name = "login", nullable = true, length = 100)
private long login;
#Column(name = "alert", nullable= true, length=500)
private String alert;
#OneToOne
#JoinColumn(name = "team_id")
private Team team;
}
Table 2 (Team):
#Entity
#Table(name = "team", uniqueConstraints = { #UniqueConstraint(columnNames = "team_name"), #UniqueConstraint(columnNames = "team_code") })
public class Team implements java.io.Serializable {
private static final long serialVersionUID = 7933770163144650730L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 80)
private String id;
#Column(name = "team_name", unique = true, nullable = false, length = 100)
private String name;
#Column(name = "team_code", unique = true, nullable = false, length = 10)
private String code;
}
Table 3 (Access):
#Entity
#Table(name = "access_def")
public class Access implements java.io.Serializable {
private static final long serialVersionUID = 7933770163144650730L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 80)
private String id;
#Column(name = "access_name", unique = true, nullable = false, length = 100)
private String name;
#Column(name = "access_code", unique = true, nullable = false, length = 10)
private String code;
}
Table 4 (Application):
#Entity
#Table(name = "application", uniqueConstraints = { #UniqueConstraint(columnNames = "name") })
public class Application implements java.io.Serializable {
private static final long serialVersionUID = 5803631085624275364L;
#Id
#Column(name = "name", nullable = false, length = 100)
private String name;
}
Table 5 (UserAppAccess):
#Entity
#Table(name = "user_app_access")
#Embeddable
public class UserAppAccess implements java.io.Serializable {
private static final long serialVersionUID = 7933770163144650730L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 80)
private String id;
#OneToOne
#JoinColumn(name = "user_id")
private UserInfo userInfo;
#Column(name = "app_name", nullable = false, length = 100)
private String appName;
#OneToOne
#JoinColumn(name = "access_id")
private Access access;
}
I have a report page that allow Admin to select multiple options (for example: list all active users in Team test and application APP1).
here is my code to fetch the data but it is not working :
public List<?> getReport(String teamId,String appName,UserStatus active,UserStatus inactive) {
Session session = sessionFactory.getCurrentSession();
String hql = "SELECT u.firstName,u.username,u.status,u.lastName,u.phone,u.team From UserInfo u,AppAccess a WHERE u.status =? OR u.status =? AND u.team.id = ? AND a.appName = :appName ";
Query query = session.createQuery(hql);
query.setParameter(0, active);
query.setParameter(1, inactive);
query.setParameter(2, teamId);
query.setParameter("appName", appName);
System.out.println(query.list());
return query.list();
}
For instance when I pass
Active Users: Active
inactive User:null
team:test
application :app1
teamId :28f66133-26c3-442b-a071-4d19d64ec0aeappName :app1active :activeinactive:null
I am getting this back from my return query.list();
[[Ljava.lang.Object;#2961116f, [Ljava.lang.Object;#23bfa3a2, [Ljava.lang.Object;#7a8ff303, [Ljava.lang.Object;#9b88d2, [Ljava.lang.Object;#6333934d, [Ljava.lang.Object;#4f0bd71c, [Ljava.lang.Object;#125797cf, [Ljava.lang.Object;#34afa071, [Ljava.lang.Object;#764e75bc, [Ljava.lang.Object;#1913c652, [Ljava.lang.Object;#61413e5a, [Ljava.lang.Object;#264b898, [Ljava.lang.Object;#22930462, [Ljava.lang.Object;#6204cfa9, [Ljava.lang.Object;#29dd9285, [Ljava.lang.Object;#11be6f3c, [Ljava.lang.Object;#6d78d53d, [Ljava.lang.Object;#17f7cff1, [Ljava.lang.Object;#e74e382, [Ljava.lang.Object;#1c047338, [Ljava.lang.Object;#68286fe6, [Ljava.lang.Object;#36ca9a76, [Ljava.lang.Object;#2f62d514, [Ljava.lang.Object;#1932c5a, [Ljava.lang.Object;#6544c984, [Ljava.lang.Object;#70a2d0d, [Ljava.lang.Object;#2d13b417, [Ljava.lang.Object;#6894691f, [Ljava.lang.Object;#6781a7dc, [Ljava.lang.Object;#7133919a]

I'd suggest using native SQL and JDBC for reporting (see
How should I use Hibernate Mapping while dealing with huge data table)
For performance reasons it is desirable to create view model objects from result set right in the DAO. It may looks like mixing levels of abstraction (view layer with persistence layer), but it's ok when you need to fetch a big amounts of data and don't want to make unnecessary object transformations from persistence models to view models.
If you're want to stuck with hibernate, you may define a syntetic entity and map if on a view, containing only necessary columns from multiple:
#Entity
#Table("V_USER_REPORT")
public class UserAppData {
// columns from table "user"
#Id
#Column(name = "id", unique = true, nullable = false, length = 100)
private String id;
#Column(name = "username", unique = true, nullable = false, length = 50)
private String username;
// columns from table "user"
#Column(name = "app_name", nullable = false, length = 100)
private String appName;
// columns from table "team"
#Column(name = "team_id", unique = true, nullable = false, length = 80)
private String team_id;
#Column(name = "team_name", unique = true, nullable = false, length = 100)
private String name;
#Column(name = "team_code", unique = true, nullable = false, length = 10)
private String code;
// and so on...
}
Then you fetch such entity by parameters as you do it with normal entity.

By adding LEFT JOIN FETCH or FETCH ALL PROPERTIES. This will force JOINS insteed of lazy initialization
String hql = "SELECT u.firstName,u.username,u.status,u.lastName,u.phone,u.team From UserInfo u,AppAccess a FETCH ALL PROPERTIES WHERE u.status =? OR u.status =? AND u.team.id = ? AND a.appName = :appName ";
More information can be found in HQL Documentation

Firstly: I hope each of your entity classes have a toString() method (can be auto-generated with eclipse) so you can print them. Printing object reference isn't enough to infer whether/not you're getting what you want.
Secondly, the syntax of HQL joins is normally like this:
String queryString = "select distinct f from Foo f inner join foo.bars as b" +
" where f.creationDate >= ? and f.creationDate < ? and b.bar = ?";
(taken from How do you create a Distinct query in HQL)

Related

Populate DTO using query with JOIN

I have this main Product table:
#Table(name = "product")
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#Column(name = "user_id", length = 20)
private Integer userId;
#Column(name = "title", length = 75)
private String title;
#Column(name = "meta_title", length = 100)
private String metaTitle;
#Column(name = "status", length = 100)
private String status;
}
Additional table for storing categories that should be returned as List:
#Table(name = "product_category")
public class ProductCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#Column(name = "product_id", length = 4)
private Integer productId;
#Column(name = "category_id", length = 20)
private Integer categoryId;
}
Additional table for storing Payment Methods that should be returned as List:
#Table(name = "product_payment_methods")
public class ProductPaymentMethods implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#Column(name = "product_id", length = 20)
private Integer productId;
#Column(name = "payment_methods", length = 20000)
private String paymentMethods;
}
I want to return a result like this:
id | title | categoryId | paymentMethods |
1 | test | 34, 43 | 345, 7, 5 |
5 | test2 | 64,5, 3 | 654, 3, 5 |
I tried this:
SELECT *
FROM Product
INNER JOIN product_category ON Product.id = product_category.productId
INNER JOIN product_payment_methods ON Product.id = product_payment_methods.productId
WHERE userId = 1
What is the proper way to populate this DTO?
public class ProductFullDTO {
private int id;
private Integer userId;
private List<Integer> categories;
private List<String> paymentMethods;
}
If, as indicated in your comments, you need query your information with HQL a good way to proceed can be the following.
First, modify your Product entity an include relationships for both ProductCategory and ProductPaymentMethods, something like:
#Table(name = "product")
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#Column(name = "user_id", length = 20)
private Integer userId;
#Column(name = "title", length = 75)
private String title;
#Column(name = "meta_title", length = 100)
private String metaTitle;
#Column(name = "status", length = 100)
private String status;
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ProductCategory> categories;
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
private List< ProductPaymentMethods> paymentMethods;
// Setters and getters, omitted for brevity
}
Modify both ProductCategory and ProductPaymentMethods to accommodate the entities relationship:
#Table(name = "product_category")
public class ProductCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
// Please, feel free to change the insertable and updatable attributes to
// fit your needs
#Column(name = "product_id", length = 4, insertable=false, updatable=false)
private Integer productId;
#ManyToOne(fetch= FetchType.LAZY)
#JoinColumn(name="product_id")
private Product product;
#Column(name = "category_id", length = 20)
private Integer categoryId;
// Setters and getters, omitted for brevity
}
#Table(name = "product_payment_methods")
public class ProductPaymentMethods implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
// Please, feel free to change the insertable and updatable attributes to
// fit your needs. By the way, why here the length is 20 and not 4?
#Column(name = "product_id", length = 20, insertable=false, updatable=false)
private Integer productId;
#ManyToOne(fetch= FetchType.LAZY)
#JoinColumn(name="product_id")
private Product product;
#Column(name = "payment_methods", length = 20000)
private String paymentMethods;
}
With this setup, as you can see in the Hibernate documentation - it is for an old Hibernate version, but it is correct today, you can use fetch joins to obtain the required information:
A "fetch" join allows associations or collections of values to be initialized along with their parent objects using a single select. This is particularly useful in the case of a collection.
For your example, consider the following HQL (assume outer join semantics, modify it as appropriate):
select product
from Product as product
left join fetch product.categories
left join fetch product.paymentMethods
where product.userId = 1
This will provide you the list of products for userId 1, with all the associated references to categories and payment methods initialized.
The conversion between the entity and the DTO should be straightforward:
Session session = ...
List<Product> products = session.createQuery(
"select product " +
"from Product as product " +
" left join fetch product.categories " +
" left join fetch product.paymentMethods " +
"where product.userId = :userId", Product.class)
.setParameter( "userId", 1)
.getResultList();
List<ProductFullDTO> productFullDTOs = null;
if (products != null) {
productFullDTOs = products.stream()
.map((product -> {
ProductFullDTO productFullDTO = new ProductFullDTO();
productFullDTO.setId(product.getId());
productFullDTO.setUserId(product.getUserId());
List<ProductCategory> categories = product.getCategories();
if (categories != null) {
List<Integer> categoriesIds = categories.stream()
.map(ProductCategory::getCategoryId)
.collect(Collectors.toList())
;
productFullDTO.setCategories(categoriesIds);
}
List<ProductPaymentMethods> paymentMethods = product.getPaymentMethods();
if (paymentMethods != null) {
List<String> paymentMethodsIds = paymentMethods.stream()
.map(ProductPaymentMethods::getPaymentMethods)
.collect(Collectors.toList())
;
productFullDTO.setPaymentMethods(paymentMethodsIds);
}
return productFullDTO;
}))
.collect(Collectors.toList())
;
}
System.out.println(productFullDTOs == null ? "No products found." : productFullDTOs.size() + " products found.");
You should use TypeHandler to finish this job. I just give paymentMethods as example.
#Results({
#Result(column = "product.id", property = "id"),
#Result(column = "user_id", property = "userId"),
#Result(column = "category_id", property = "categories"),
#Result(column = "payment_methods", property = "paymentMethods" ,typeHandler= StrListTypeHandler.class),
})
#Select("SELECT * FROM Product INNER JOIN product_category ON Product.id = product_category.productId "
+ " INNER JOIN product_payment_methods ON Product.id = product_payment_methods.productId "
+ " WHERE userId = 1")
List<ProductFullDTO> getProduct();
// the below is TypeHandler implementation
#Component
public class StrListTypeHandler implements TypeHandler<List<String>> {
#Override
public void setParameter(PreparedStatement preparedStatement, int i, List<String> strings, JdbcType jdbcType) throws SQLException {
StringBuffer sb = new StringBuffer();
for (String s : strings) {
sb.append(s).append(",");
}
preparedStatement.setString(i, sb.toString().substring(0, sb.toString().length() - 1));
}
#Override
public List<String> getResult(ResultSet resultSet, String s) throws SQLException {
String[] arr = resultSet.getString(s).split(",");
return Arrays.asList(arr);
}
#Override
public List<String> getResult(ResultSet resultSet, int i) throws SQLException {
String[] arr = resultSet.getString(i).split(",");
return Arrays.asList(arr);
}
#Override
public List<String> getResult(CallableStatement callableStatement, int i) throws SQLException {
String[] arr = callableStatement.getString(i).split(",");
return Arrays.asList(arr);
}
}

JPA Map ManyToOne with JoinColumn over FEDERATED mysql table

I have a big problem, we are working now on implementing single sing in a project. Our admin made some tables as FEDERATED in DB (mysql) and because of this app is throwing a lots of exception. How can I map in JPA a field with ManyToOne relation to that federated table ? It's throwing exceptions on update and on insert in table users for column LOCALITY_ID (where it must have only the id).
Error:
javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value: ......stuff
public class User extends BaseEntity implements IAccessDataAware, UserDetails, IAuditable {
/** serial version */
private static final long serialVersionUID = 2899839338023778586L;
#Embedded
private AccessData accessData = new AccessData();
#Column(name = "FIRST_NAME", length = Constants.COLUMN_NORMAL_LENGTH, nullable = false)
private String firstName;
#Column(name = "LAST_NAME", length = Constants.COLUMN_NORMAL_LENGTH, nullable = false)
private String lastName;
#Column(name = "CNP", length = Constants.COLUMN_CNP_LENGTH, nullable = true)
private String cnp;
#Column(name = "EMAIL", length = Constants.COLUMN_NORMAL_LENGTH, nullable = false)
private String email;
#Column(name = "PHONE", length = Constants.COLUMN_NORMAL_LENGTH)
private String phone;
#Column(name = "USERNAME", length = Constants.COLUMN_NORMAL_LENGTH, nullable = false)
private String userName;
#Column(name = "PASSWORD", length = Constants.COLUMN_MD5_ENCRYPTED_LENGTH, nullable = false)
private String password;
#Column(name = "PASSWORD_OAUTH", length = Constants.COLUMN_ENCRYPTED_LENGTH, nullable = true)
private String passwordOAuth;
#Column(name = "SALT_OAUTH", length = Constants.COLUMN_ENCRYPTED_LENGTH, nullable = true)
private String salt;
#Column(name = "PASSWORD_OAUTH_ALGORITHM", length = Constants.COLUMN_ENCRYPTED_LENGTH, nullable = true)
private String passwordOAuthAlgorithm;
#Column(name="digital_signature")
private Boolean digitalSignature=Boolean.FALSE;
#Column(name = "DESCRIPTION", length = Constants.COLUMN_DESCRIPTION_LENGTH)
private String description;
#Column(name = "PATH_AVATAR_IMAGE", length = Constants.COLUMN_NORMAL_LENGTH)
private String pathAvatarImage;
#Column(name = "PATH_AVATAR_IMAGE_ORIGINAL", length = Constants.COLUMN_NORMAL_LENGTH)
private String pathAvatarImageOriginal;
#ManyToOne(optional = false)
#JoinColumn(name = "ROLE_ID")
private Role role;
#OneToMany(mappedBy = "user")
private List<HistoryEvent> historyEvents;
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "LOCALITY_ID")
private Locality locality;
....... Getters and Setters
}
Thank you

"Save transient object before flushing" error [duplicate]

This question already has answers here:
How to fix the Hibernate "object references an unsaved transient instance - save the transient instance before flushing" error
(33 answers)
Closed 7 years ago.
I tried to save one of my entity Site containing a User, this User is registered in database and I don't want to save it too. The problem is when I try to save the site, I get the following error :
org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: com.project.netmg.bo.impl.User; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.project.netmg.bo.impl.User
I think, it try to save the user too but it's not that I want. I just want to search the good user and assign it to the site.
java code :
public String saveSite(#Valid #ModelAttribute SiteForm siteForm, BindingResult bindingResult, Model uiModel) {
populateSiteforSave(siteForm);
_siteService.saveSite(siteForm.getSite());
return WebConstants.REDIRECT_TO_VIEW_SITE_URL + siteForm.getSite().getSiteName();
}
private void populateSiteforSave(SiteForm siteForm) {
siteForm.getSite().setCountry((Country) _countryService.getCountryByName(siteForm.getSite().getCountry().getName()));
siteForm.getSite().setBusiness((Business) _businessService.getBusinessById(siteForm.getSite().getBusiness().getId()));
siteForm.getSite().setStatus((Status) _statusService.getStatusById(siteForm.getSite().getStatus().getId()));
if (!siteForm.getLocalItFullName().isEmpty()) {
siteForm.getSite().setLocalIt(_userService.findUserByUserFullName(siteForm.getLocalItFullName())); // user
} else {
siteForm.getSite().setLocalIt(null);
}
if (!siteForm.getRifFullName().isEmpty()) {
siteForm.getSite().setRif(_userService.findUserByUserFullName(siteForm.getRifFullName())); //user
} else {
siteForm.getSite().setRif(null);
}
if (siteForm.getSite().getLocalContact().getId() != null) {
siteForm.getSite().setLocalContact((User) _userService.findUserByUsername(siteForm.getSite().getLocalContact().getUsername())); //user
}
}
Site class:
#Entity
#Table(name = "SITE", uniqueConstraints = { #UniqueConstraint(columnNames = { "SITE_COUNTRY_ID", "SITE_NAME" }) })
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED, withModifiedFlag = true)
public class Site implements ISite {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = -390717603276436784L;
/** The id. */
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "SITE_ID", unique = true, nullable = false)
private long id;
/** The site address. */
#Column(name = "SITE_ADDRESS", length = BusinessConstants.SITE_ADDRESS)
private String address;
/** The site analog phone number. */
#Column(name = "SITE_ANALOG_PHONE_NUMBER", length = BusinessConstants.SITE_ANALOG_PHONE_NUMBER)
private String analogPhoneNumber;
/** The site comment. */
#Column(name = "SITE_COMMENT", length = BusinessConstants.SITE_COMMENT)
private String comment;
/** The site entity code. */
#Digits(integer = 3, fraction = 0, message = "Please enter max 3 digits")
#Column(name = "SITE_ENTITY_CODE", nullable = false)
private long entityCode;
/** The site invoice code. */
#Digits(integer = 10, fraction = 0, message = "Please enter max 10 digits")
#Column(name = "SITE_INVOICE_CODE", nullable = false)
private long invoiceCode;
/** The site local it phone. */
#Column(name = "SITE_LOCAL_IT_PHONE", length = BusinessConstants.SITE_LOCAL_IT_PHONE)
private String localItPhone;
/** The site name. */
#NotBlank
#Column(name = "SITE_NAME", nullable = false, length = BusinessConstants.SITE_NAME)
private String siteName;
/** The site subnet. */
#NotBlank
#Column(name = "SITE_SUBNET", nullable = false, length = BusinessConstants.SITE_SUBNET)
private String subnet;
/** The site user number. */
#Digits(integer = 4, fraction = 0, message = "Please enter max 4 digits")
#Column(name = "SITE_USER_NUMBER")
private Long userNumber;
/** The business. */
#Valid
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SITE_BUSINESS_ID", nullable = false)
private Business business;
/** The country. */
#Valid
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "SITE_COUNTRY_ID")
private Country country;
/** The local contact. */
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SITE_LOCAL_CONTACT", nullable = true)
private User localContact;
/** The local it. */
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SITE_LOCAL_IT", nullable = true)
private User localIt;
/** The rif. */
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SITE_RIF", nullable = true)
private User rif;
/** The status. */
#Valid
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SITE_STATUS_ID", nullable = false)
private Status status;
/** The end date. */
#Column(name = "SITE_END_DATE")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(iso = ISO.DATE_TIME)
private Date endDate = null;
#ManyToMany(targetEntity = User.class, mappedBy = "userSites", fetch = FetchType.LAZY)
#NotAudited
private Set<IUser> siteUsers;
User class :
#Entity
#Table(name = "USERS")
public class User implements IUser {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 6741623705511494367L;
private static final String USER_ID = "USER_ID";
/** The user id. */
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = USER_ID)
private Long id;
/** The user first name. */
#Column(name = "FIRSTNAME", length = BusinessConstants.USER_FIRSTNAME, nullable = false)
#NotNull
private String userFirstName;
/** The user last name. */
#Column(name = "LASTNAME", length = BusinessConstants.USER_LASTNAME, nullable = false)
#NotNull
private String userLastName;
/** The user email. */
#Column(name = "EMAIL", length = BusinessConstants.USER_EMAIL)
#NotNull
private String userEmail;
/** The user uid. */
#Column(name = "LOGIN", length = BusinessConstants.USER_LOGIN, nullable = false, unique = true)
#NotNull
private String username;
#Valid
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PROFILE_ID", referencedColumnName = "PROF_ID", nullable = false)
private Profile profile;
#BatchSize(size = 20)
#ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY,targetEntity=Site.class)
#JoinTable(name = "USER_SITE",
joinColumns = { #JoinColumn(name = USER_ID, nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "SITE_ID", nullable = false) })
private Set<ISite> userSites;
#ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY,targetEntity=Region.class)
#JoinTable(name = "USER_REGION",
joinColumns = { #JoinColumn(name = USER_ID, nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "REGION_ID", nullable = false) })
private Set<IRegion> userRegions;
#BatchSize(size = 20)
#ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY,targetEntity=Zone.class )
#JoinTable(name = "USER_ZONE",
joinColumns = { #JoinColumn(name = USER_ID, nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "ZONE_ID", nullable = false) })
private Set<IZone> userZones;
#BatchSize(size = 20)
#ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY,targetEntity=Country.class )
#JoinTable(name = "USER_COUNTRY",
joinColumns = { #JoinColumn(name = USER_ID, nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "COUNTRY_ID", nullable = false) })
private Set<ICountry> userCountries;
#Transient
private Collection<? extends GrantedAuthority> authorities;
#Transient
private String userFullName;
You can't really have it both ways. If the object graph includes the User, then it will (have to) be persisted if changed in your code. Have you considered what it evens means to fetch a Site (including a User), then change the localContact and persist the Site again?
If you want the localContact to be settable in the object graph, but not persisted, then it can be annotated with #Transient:
/** The local contact. */
#Transient
private User localContact;
Hope that helps.
Cheers,
Have you annotate cascade=CascadeType.ALL to your entity object mapping
eg :
#ManyToOne(cascade = CascadeType.ALL)
private String entityType;

Which data format is suitable to transfer data (result of left join db query) from servlet to JSP?

[using JPA, MySQL, MVC, Servlets, JSP]
If I read some data from database LEFT JOIN-ing three tables (inside method of DAO object) how should I format that result, so i can set it as request attribute (in servlet) and forwards to JSP page?
Entities(tables in db):
Post:
#Entity
#Table(name = "post")
public class Post implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "post_id", unique = true, nullable = false)
private Integer id;
#Column(name = "post_title", length=300, unique = false, nullable = false)
private String title;
#Column(name = "post_date", unique = false, nullable = false)
private Date date;
#Column(name = "post_summary", length=1000, unique = false, nullable = true)
private String summary;
#Column(name = "post_content", length=50000, unique = false, nullable = false)
private String content;
#Column(name = "post_visitors", unique = false, nullable = false)
private Integer visitors;
#ManyToOne
#JoinColumn (name = "user_id", referencedColumnName="user_id", nullable = false)
private User user;
#ManyToOne
#JoinColumn (name = "category_id", referencedColumnName="category_id", nullable = false)
private Category category;
#OneToMany(cascade = { ALL }, fetch = LAZY, mappedBy = "post")
private Set<Comment> comments = new HashSet<Comment>();
...
Entity Comment:
#Entity
#Table(name = "comment")
public class Comment implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "comment_id", unique = true, nullable = false)
private Integer id;
#Column(name = "comment_title", length=300, unique = false, nullable = false)
private String title;
#Column(name = "comment_date", unique = false, nullable = false)
private Date date;
#Column(name = "comment_content", length=600, unique = false, nullable = false)
private String content;
#ManyToOne
#JoinColumn (name = "user_id", referencedColumnName="user_id", nullable = false)
private User user;
#ManyToOne
#JoinColumn (name = "post_id", referencedColumnName="post_id", nullable = false)
private Post post;
...
Entity User:
#Entity
#Table(name = "user")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer id;
#Column(name = "user_name", length=45, unique = false, nullable = false)
private String name; // "first_name" + ' ' + "last_name"
//URL address to user's image
#Column(name = "user_image", length=500, unique = false, nullable = true)
private String image;
#Column(name = "user_username", length=45, unique = false, nullable = false)
private String username;
#Column(name = "user_password", length=45, unique = false, nullable = false)
private String password;
...
So, I would like to make a method, probably inside PostDAO object that will look something like this:
public <SomeDataTypeFormat???> getPostsSummaries(){
Query q = em.createNativeQuery("SELECT
post_title,
post_summary,
post_date,
COUNT(comment_id) AS comment_cnt,
user.user_name
FROM
post
LEFT JOIN user USING(user_id)
LEFT JOIN comment USING(post_id)
GROUP BY
post_id
ORDER BY
comment_cnt DESC");
...
}
Method returns some fields from all three tables in database.
Do I need to make separate class and store those data in objects of that class? Or JSON (although I haven't worked with it yet)?
What is the practice? What is easiest data format to use and forward from servlet to JSP, for some fields gotten as a result of joining couple tables?
It depends on your objective; to get the data to the browser, JSON and AJAX isn't a bad choice. To get the data to the JSP (from the Servlet), you'll probably want a Data Transfer Object (or possibly an immutable Value Object).

ManyToMany mapping in JPA

I have a M:N relationship between table Users and Groups. Now I am trying to join those two tables using JPA and I always get this exception:
Multiple writable mappings exist for the field [GROUPS.name]. Only one may be defined as writable, all others must be specified read-only.
Here is my Users class (I didn't enclose getters and setters for brevity), it's implemented by Admin class and SignedUser class with some additional properties.
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Users implements Serializable {
#Id
#Column(name = "login", nullable = false, length = 10)
private String login;
#Column(name = "name", nullable = false, length = 30)
private String name;
#Column(name = "surname", nullable = false, length = 50)
private String surname;
#Column(name = "email", nullable = false, length = 100)
private String email;
#Column(name = "password", nullable = false, length = 20)
private String password;
#ManyToMany(mappedBy="users")
private List<Groups> groups;
and this is the Group class:
#Entity
public class Groups implements Serializable {
#Id
#Column(name = "name", nullable = false, length = 20)
private String groupName;
#Column(name = "name", nullable = false, length = 50)
private String descr;
#ManyToMany
#JoinTable(name = "user_group",
joinColumns = {#JoinColumn(name = "groupName")},
inverseJoinColumns = {#JoinColumn(name = "login")})
private List<Users> users;
I've also tried to put JoinTable annotation into Users class but it ended with the same result. Thanks in advance for any advices.
In the class Group you have two mappings for column name as properties groupName and descr.
The error is not related to the ManyToMany.

Categories

Resources