Creating unique and merging existing row in database with JPA - java

I have 2 tables: User and Loan.
User have 3 fields: id (PK), first_name and last_name. Loan table have field user_id that is foreign key to User table:
By persisting a new Loan I need to create new User if his first_name and last_name are unique, otherwise put his id in uder_id.
The source code of my Loan class:
public class Loan {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private Long sum;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private User user;
... methods ...
I'm using this method to persist new User:
#PersistenceContext
private EntityManager em;
public void save(User user) {
if (user.getId() == null) {
em.persist(user);
} else {
em.merge(user);
}
}
And when I try to save a new Loan it always persists a new User with the same first_name and last_name but different id.
loan.setSum(sum);
loan.setUser(new User(firstName, lastName));
loanService.save(loan);
To use user's first_name and last_name as a PK is not a solution, I need an id.
UPDATE_1
I tried to find User by his name:
public User findByName(String firstName, String lastName) {
TypedQuery<User> query = em.createQuery(
"SELECT u FROM User u WHERE u.firstName = :firstName " +
"AND u.lastName = :lastName", User.class)
.setParameter("firstName", firstName).setParameter("lastName", lastName);
return query.getSingleResult();
}
But when I enter new user I got an exception:
javax.faces.FacesException: #{loanBean.requestLoan()}: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
And when I enter existing user, it adds a new one with the same firstName and lastName but new id.
When I repeat this operation I got another exception:
javax.servlet.ServletException: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
UPDATE_2
Thanks a lot Pietro Boido for your really helpful suggestions.I created unique index on first_name and last_name fields in DB and refactor the save() method. But now when I enter existing user's data I got new exception
javax.servlet.ServletException: org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'FIRST_LAST_NAME' defined on 'USER'.
Error Code: 20000

You should first execute a query to find the user by name and create a new one only if it was not found:
User user = userService.find(firstName, lastName);
if (user == null) {
user = loanService.createUser(new User(firstName, lastName));
}
loan.setSum(sum);
loan.setUser(user);
loanService.save(loan);
Since there might be no user with the given name, use getResultList when you query for the user, because getSingleResult expects to always find a result.
List<User> users = query.getResultList();
if (!users.isEmpty()) {
return users.iterator().next();
} else {
return null;
}
The code assumes that the database has a unique index on first_name, last_name.
You should not cascade operations on the ManyToOne relationship. Think about this: if you delete a loan, should the user be deleted too?
Cascading operations should be used when the related entity is part of the relating entity and their life cycles are managed together.
#Entity
#Table(name="loans")
public class Loan implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "total")
private Long sum;
#ManyToOne()
#JoinColumn(name = "user_id")
private User user;
//...
}
Here is a possible working example:
#Stateless
public class LoanService implements LoanServiceRemote {
#PersistenceContext
private EntityManager em;
#Override
public User createUser(User user) {
em.persist(user);
return user;
}
#Override
public Loan createLoan(Loan loan) {
em.persist(loan);
System.out.println("loan persisted: id=" + loan.getId());
return loan;
}
#Override
public Loan saveLoan(Loan loan) {
em.merge(loan);
return loan;
}
#Override
public Long incrementLoan(Integer loanId, long amount) {
Loan loan = em.find(Loan.class, loanId);
if (loan != null) {
long sum = loan.getSum() + amount;
/*
* The entity is bound to the entity manager,
* because it was returned by the find method.
* We can simply set its properties and
* the entity manager will update the datasource
* after the method returns and the transaction commits.
* No need to call persist or merge.
*/
loan.setSum(sum);
return sum;
}
return null;
}
#Override
public boolean deleteLoan(Integer loanId) {
Loan loan = em.find(Loan.class, loanId);
if (loan != null) {
em.remove(loan);
return true;
}
return false;
}
#Override
public Loan findLoan(Integer loanId) {
return em.find(Loan.class, loanId);
}
#Override
public List<Loan> requestLoans(LoanRequest loanRequest) {
User user;
TypedQuery<User> query = em.createQuery("select user from User user where user.firstName = :firstName and user.lastName = :lastName", User.class);
query.setParameter("firstName", loanRequest.getFirstName());
query.setParameter("lastName", loanRequest.getLastName());
List<User> users = query.getResultList();
if (users.isEmpty()) {
user = new User();
user.setFirstName(loanRequest.getFirstName());
user.setLastName(loanRequest.getLastName());
//new entities must be persisted
em.persist(user);
} else {
user = users.get(0);
}
List<Loan> loans = new ArrayList<>();
Long[] totals = loanRequest.getTotals();
for (int i = 0; i < totals.length; i++) {
Loan loan = new Loan();
loan.setSum(totals[i]);
loan.setUser(user);
em.persist(loan);
loans.add(loan);
}
return loans;
}
}
A unit test:
#Test
public void testLoan() {
User user = loanService.createUser(newUser());
Loan loan1 = new Loan();
loan1.setSum(10L);
loan1.setUser(user);
Loan loan2 = loanService.createLoan(loan1);
assertNotNull(loan2);
Integer loanId = loan2.getId();
assertNotNull(loanId);
assertEquals(loan1.getSum(), loan2.getSum());
assertEquals(loan1.getUser(), user);
User user2 = loanService.createUser(newUser());
loan2.setUser(user2);
loan2.setSum(20L);
Loan loan3 = loanService.saveLoan(loan2);
assertLoanEquals(loan2, loan3);
Long total = loanService.incrementLoan(loanId, 10L);
assertNotNull(total);
assertEquals((Long)(loan3.getSum() + 10L), total);
loan3.setSum(total);
Loan loan4 = loanService.findLoan(loanId);
assertLoanEquals(loan3, loan4);
boolean result = loanService.deleteLoan(loanId);
assertTrue(result);
Loan loan5 = loanService.findLoan(loanId);
assertNull(loan5);
Long[] totals = new Long[]{1L,2L,3L};
LoanRequest loanRequest = new LoanRequest();
loanRequest.setFirstName("Amerigo");
loanRequest.setLastName("Vespucci");
loanRequest.setTotals(totals);
List<Loan> loans = loanService.requestLoans(loanRequest);
assertNotNull(loans);
assertEquals(3, loans.size());
for (int i = 0; i < 3; i++) {
assertEquals(totals[i], loans.get(i).getSum());
loanService.deleteLoan(loans.get(i).getId());
}
}
void assertLoanEquals(Loan loan1, Loan loan2) {
assertNotNull(loan1);
assertNotNull(loan2);
assertEquals(loan1.getSum(), loan2.getSum());
assertUserEquals(loan1.getUser(), loan2.getUser());
assertEquals(loan1.getId(), loan2.getId());
}
void assertUserEquals(User user, User user2) {
assertNotNull(user);
assertNotNull(user2);
assertEquals(user.getId(), user2.getId());
assertEquals(user.getFirstName(), user2.getFirstName());
assertEquals(user.getLastName(), user2.getLastName());
}

Related

Hibernate query to get a different user table data not working

I am trying to simply fetch the records from a table with the following scenario:
User: abc is the one with which I am loggin into my db it has the rights to select.
Table I am trying to access is xyz.customer, DB user xyz has this table customer.
The error that I am getting is that entity not found. even the I have clearly mentioned my class in the package scan. I tried making SqlResultSetMapping and then it said not found again. I put it in another entity class which is working fine and it still said SqlResultSetMapping not found. My code is as follow:
The code where I am calling it and gives error:
List<SampleClass> sampleClass=
entityIBSManager.createNativeQuery("select * from xyz.customer","CustomerMapping").getResultList();
The code of my entity class:
#Entity
#Table(name = "CUSTOMER", catalog = "XYZ")
#NamedQuery(name = "SampleClass.findAll", query = "select p from SampleClass p")
#SqlResultSetMapping(
name = "CustomerMapping",
entities = #EntityResult(
entityClass = SampleClass.class,
fields = {
#FieldResult(name = "customerNo", column = "CUSTOMER_ID"),
#FieldResult(name = "countryCode", column = "COUNTRY_CODE"),
#FieldResult(name = "status", column = "STATUS")}))
public class SampleClass implements Serializable {
#Id
#Column(name="CUSTOMER_ID")
private Long customerNo;
#Id
#Column(name="COUNTRY_CODE")
private String countryCode;
#Column(name="STATUS")
private int status;
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public Long getCustomerNo() {
return customerNo;
}
public void setCustomerNo(Long customerNo) {
this.customerNo = customerNo;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
#Override
public String toString() {
return "PurgeCustomersIBS [customerNo=" + customerNo + ", countryCode=" + countryCode + ", status=" + status + "]";
}
}
In My DB table I have a composite key combination of country code and customer no.
and I have tried using the direct call to my named query and it gives the error of no named query found.
Thankyou for your help in advance.
Here is my stacktrace
07:57:12.006 [readTask_Worker-3] ERROR org.quartz.core.JobRunShell - Job DEFAULT.cSVFileJob threw an unhandled Exception: java.lang.IllegalArgumentException: No query defined for that name [PurgeCustomersIBS.findAll] at org.hibernate.jpa.spi.AbstractEntityManagerImpl.buildQueryFromName(AbstractEntityManagerImpl.java:753) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final] at org.hibernate.jpa.spi.AbstractEntityManagerImpl.createNamedQuery(AbstractEntityManagerImpl.java:890) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]

JPA: "Can't write; duplicate key in table" for a One-to-One relationship

I have these entities that I want to relate bi-directionaly.
Credential:
#Entity
#Access(AccessType.PROPERTY)
#Table(name = "credential")
public class Credential extends MetaInfo implements Serializable {
...
private Email email;
...
#OneToOne(cascade = CascadeType.ALL, optional = false, orphanRemoval = true)
#JoinColumn(name="email", referencedColumnName="email_address")
public Email getEmail() {
return email;
}
public void setEmail(Email email) {
this.email = email;
}
...
}
Email:
#Entity
#Access(AccessType.PROPERTY)
#Table(name = "email")
public class Email extends MetaInfo implements Serializable{
...
private Credential credential;
public Email() {
}
public Email(String emailAddress) {
this.emailAddress = emailAddress;
}
#Id
#Column(name="email_address")
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
#OneToOne(mappedBy = "email", optional=false)
public Credential getCredential() {
return credential;
}
public void setCredential(Credential credential) {
this.credential = credential;
}
}
In a CredentialRepository class I am testing whether the passed-in email
is not assigned to any user except for the user with the username passed-in as the second (optional) parameter:
#Override
public boolean emailIsAssigned(String... args) {
assert(args.length > 0);
if(InputValidators.isValidEmail.test(args[0])){
EntityManager em = entityManagerFactory.createEntityManager();
try {
TypedQuery<Long> count = em.createQuery("SELECT COUNT(e) "
+ "FROM Email e WHERE e.emailAddress "
+ "= :email AND e "
+ "IN (SELECT c.email FROM Credential c WHERE c.username "
+ "!= :username)", Long.TYPE).setParameter("email", args[0])
.setParameter("username", null);
if(InputValidators.stringNotNullNorEmpty.apply(args[1])){
//only if the username has been provided
count.setParameter("username", args[1]);
}
return count.getSingleResult() > 0;
} catch (Exception e) {
System.out.println(e.getMessage());
return false;
} finally {
em.close();
}
}else{
throw new NotAValidEmailException(args[0] + " is not a"
+ " valid email address.");
}
}
Thus above args[0] is the email under test and args[1] is the username under test.
And this is the test that is causing me problems (note that before I already successfully tested inserts, updates and even the emailIsAssigned method but without the c.email part which seems to cause the issue:
#Test
public void emailAlreadyExistsTest(){
assertTrue(credentialRepo.emailIsAssigned("existing_email#yahoo.ca"));
}
And this is the error message that I have:
[EL Warning]: 2017-04-17 17:55:33.606--ServerSession(234430897)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Can't write; duplicate key in table '#sql-3e4_9a'
Error Code: 1022
Call: ALTER TABLE credential ADD CONSTRAINT FK_credential_email FOREIGN KEY (email) REFERENCES email (email_address)
Query: DataModifyQuery(sql="ALTER TABLE credential ADD CONSTRAINT FK_credential_email FOREIGN KEY (email) REFERENCES email (email_address)")
I would appreciate if someone could give me a piece of advice. I could always just change the email into a String and mark it as "unique" in #Column, but I feel that there is no reason for the chosen approach not to work.
I am using MySQL as the DB vendor, and Eclipse-Link JPA implementation. I did try to "hard-change" the name of the FK constraint but to no avail. The DB and all tables have the same collation (utf8_unicode_ci).
Try to delete the primary key for class Email because "extends MetaInfo"

Column count does not match; SQL statement (org.h2.jdbc.JdbcSQLException) - At inserting to table with NamedParameterJdbcTemplate

I want to save User instance to H2 DB.
And I have got following exception for saving new user to DB:
Caused by: org.h2.jdbc.JdbcSQLException: Column count does not match; SQL statement:
INSERT INTO Users (user_id, user_name, user_birthday, user_email, user_role, user_tickets)
VALUES (?, ?, ?, ?, ?, ) [21002-191]
Here is DAO snippet:
#Override
public Integer create(User entity) {
String sql = "INSERT INTO Users (user_id, user_name, user_birthday, user_email, user_role, user_tickets) " +
"VALUES (:id, :name, :birthday, :email, :role, :tickets)";
SqlParameterSource parameterSource =
new MapSqlParameterSource("id", entity.getId())
.addValue("name", entity.getName())
.addValue("birthday", entity.getBirthday())
.addValue("email", entity.getEmail())
.addValue("role", entity.getRole())
.addValue("tickets", entity.getBookedTickets());
Logger.info("Create user: " + entity);
return getNamedParameterJdbcTemplate().update(sql, parameterSource); <== It fails here
}
SQL script for creating DB looks as follows:
----------------------
-- Create Users table
----------------------
CREATE TABLE Users (
user_id INTEGER PRIMARY KEY NOT NULL,
user_name VARCHAR(30) NULL,
user_birthday DATETIME NULL,
user_email VARCHAR(30) NULL,
user_role VARCHAR(20) NULL,
user_tickets VARCHAR(100) NULL,
);
-----------------------
-- Create Tickets table
-----------------------
CREATE TABLE Tickets (
tick_id INTEGER PRIMARY KEY NOT NULL,
event_id VARCHAR(30),
tick_price DECIMAL(8,2),
user_id INTEGER,
);
Here is User POJO:
public class User {
private Integer id;
private String name;
private Calendar birthday;
private String email;
private String role;
private Set<Ticket> bookedTickets = new HashSet<>();
// getters / setters
I suppose that it can't write to Set<Ticket>, but I don't know how to resolve this issue.
UPDATE:
For performing DB access, I am using - Spring JDBC.
Exactly NamedParameterJdbcTemplate:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean class="net.lelyak.edu.dao.NamedParameterJdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
public class NamedParameterJdbcDaoImpl extends NamedParameterJdbcDaoSupport {
#Autowired
private DataSource dataSource;
#PostConstruct
private void initialize() {
setDataSource(dataSource);
}
}
DAO implementation:
#Repository
public class UserDaoImpl extends NamedParameterJdbcDaoImpl implements IGenericDao<User, Integer> {
#Override
public Integer create(User entity) {
// todo move SQL queries to utility class
String sql = "INSERT INTO Users (user_id, user_name, user_birthday, user_email, user_role, user_tickets) " +
"VALUES (:id, :name, :birthday, :email, :role, :tickets)";
// see create() at above text
Any suggestion?
Column user_tickets is VARCHAR(100), but the value you assign to :tickets is a Set<Ticket>, so how is that supposed to work?
Spring doesn't know what you are doing, but it assumes that you're building an IN clause when using a multi-valued argument, e.g. x IN (:tickets), so it replaces the :tickets with the appropriate number of parameter markers. E.g. if your set had 3 values, it would become x IN (?,?,?).
Your Set is empty, so no markers are generated. Technically, I think it should have thrown an exception, because that wouldn't be valid even for an IN clause, but it doesn't.
So, what do you expect the value of column user_tickets to be if your Set<Ticket> had values? The string version of the Set, e.g. [Ticket1, Ticket2]? If so, then call toString().
.addValue("tickets", entity.getBookedTickets().toString());
Then cross your fingers and hope that won't exceed 100 characters.
Sorry, I have forgotten.
My assumptions are user can have many tickets and one tickets belong to only one user. You cant save on database cell all collection, so solution is change relation and save user id on ticket. Bellow is all. I created service class, wich check if you have user on database, if not - save it.
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private Calendar birthday;
private String email;
private String role;
#Transient
private Set<Ticket> bookedTickets = new HashSet<>(); //I cant save collection into database
//getters and setters
}
#Entity
public class Ticket {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String desc;
private int number;
/** id of customer as owner this ticket */
#ManyToOne
private User user;
//getters and setters
}
in userDAO method to save:
public void save(User user){
String sql = "INSERT INTO User ( name, birthday, email, role) VALUES (:name, :birthday, :email, :role)";
SqlParameterSource parameterSource =
new MapSqlParameterSource("name", user.getName())
.addValue("birthday", user.getBirthday())
.addValue("email", user.getEmail())
.addValue("role", user.getRole());
namedParameterJdbcTemplate.update(sql, parameterSource);
sql="SELECT id FROM User WHERE name = :name AND birthday=:birthday AND email=:email AND role=:role";
Integer id = namedParameterJdbcTemplate.query(sql, parameterSource, new ResultSetExtractor<Integer>() {
#Override
public Integer extractData(ResultSet result) throws SQLException,DataAccessException {
return result.getInt("id");
}
});
user.setId(id);
}
in ticketDAO method for save:
public void save(Ticket ticket){
String sql = "INSERT INTO Ticket (desc , number, user_id) VALUES (:desc, :number, :userId)";
SqlParameterSource parameterSource =
new MapSqlParameterSource("desc", ticket.getDesc())
.addValue("number", ticket.getNumber())
.addValue("userId", ticket.getUser().getId());
namedParameterJdbcTemplate.update(sql, parameterSource);
}
and service for saveTickets:
public class UserService {
private TicketDAO ticketDAO;
private UserDAO userDAO;
public void saveTicketsForUser(User user){
if(user.getId()==null){
//if user is not saved in database
userDAO.save(user);
}else{
//if you have this client in database, you don't need to save client
}
for(Ticket ticket: user.getBookedTickets()){
ticket.setUser(user);
ticketDAO.save(ticket);
}
}
}
you can inject dao classes into service using xml.
The solution was with redesign the logic a little bit. Moving main part saving logic to parent abstract class:
public abstract class BaseDAO<ENTITY extends BaseEntity> extends NamedParameterJdbcDaoSupport implements IGenericDao<ENTITY> {
private final String tableName;
private final Class<ENTITY> entityClass;
private final List<String> fields;
private final String insertSQL;
private final String updateSQL;
public BaseDAO(Class<ENTITY> entityClass, String tableName, List<String> fields) {
this.entityClass = entityClass;
this.tableName = tableName;
this.fields = fields;
// init SQLs
StringBuilder sbInsertSQL = new StringBuilder();
StringBuilder sbUpdateSQL = new StringBuilder();
sbInsertSQL.append("INSERT INTO ").append(tableName).append(" (");
sbUpdateSQL.append("UPDATE ").append(tableName).append(" SET ");
for (int i = 0; i < fields.size(); i++) {
if (i > 0) {
sbInsertSQL.append(", ");
sbUpdateSQL.append(", ");
}
sbInsertSQL.append(fields.get(i));
sbUpdateSQL.append(fields.get(i)).append("=:").append(fields.get(i));
}
sbInsertSQL.append(") ").append("VALUES (");
for (int i = 0; i < fields.size(); i++) {
if (i > 0) {
sbInsertSQL.append(",");
}
sbInsertSQL.append(":").append(fields.get(i));
}
sbInsertSQL.append(")\n");
sbUpdateSQL.append(" WHERE id=:id\n");
this.insertSQL = sbInsertSQL.toString();
this.updateSQL = sbUpdateSQL.toString();
Logger.debug("BaseDAO(), insertSQL: [" + insertSQL + "]");
Logger.debug("BaseDAO(), updateSQL: [" + updateSQL + "]");
}
#Override
public Long save(ENTITY entity) {
long res;
if (entity.getId() == null) {
res = insert(entity);
} else {
update(entity);
res = entity.getId();
}
return res;
}
Child DAO will have a look like following:
public class UserDAO extends BaseDAO<User> {
private static final String USER_TABLE_NAME = "t_user";
private static final String userFields[] = {"name", "birthday", "email", "password", "role", "enabled"};
public UserDAO() {
super(User.class, USER_TABLE_NAME, Arrays.asList(userFields));
}
Also, make user table with auto-incrementing id:
----------------------
-- create t_user table
----------------------
CREATE TABLE t_user (
id INT GENERATED ALWAYS AS IDENTITY CONSTRAINT pk_user PRIMARY KEY,
name VARCHAR(60) NOT NULL,
birthday DATE,
email VARCHAR(60),
password VARCHAR(100),
role VARCHAR(300),
enabled SMALLINT(6)
);
Also, the model should be updated with parent logic:
public abstract class BaseEntity {
protected Long id = null;
protected String name;
public class User extends BaseEntity {
private Date birthday;
private String email;
private String password;
private String role;
private boolean enabled;

after setting attribute for HttpSession it becomes Null

I'm developing a Sign up operation, where the user enter his email and password press the submit(createUser method) button, User entity get persisted AND set the id of User in HttpSession, next he moved to next jsf where enter information for School Object (Entity). My problem is that the User get persisted BUT the School is not. here is my code:
public CreateBn() {
user = new User();
school = new School();
adress = new Adresse();
school.setAdresse(adress);
facesContext = FacesContext.getCurrentInstance();
session = (HttpSession) facesContext.getExternalContext().getSession(false);
}
public String createUser() {
initialiserDateInscription();
session.setAttribute("UserId", user.getId());
//System.out.println((BigInteger) session.getAttribute("UserId"));
userPr.createUser(user);
return SHCOOL_INSCRIPTION;
}
public String createSchool() {
BigInteger userId = (BigInteger) session.getAttribute("UserId");
System.out.println("MEHDI : " + userId);
try {
User userTemp = userPr.getUserById(userId);// Here is the problem
school.setUser(userTemp);
} catch (Exception e) {
e.printStackTrace();
}
session.setAttribute("SchoolId", school.getId());
school.setAdresse(adress);
schoolPr.createSchool(school);
return INSCRIPTION_RETURN;
}
as you can see I get the User entity based on the UserId saved in session, but I get nothing, it says:
javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
any more info, I'm here. So how can I solve this??
#Entity
#Table(schema = "school", name = "school")
public class School implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private BigInteger id;
private String name;
#OneToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "userId")
private User user;
#OneToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
#JoinColumn(name = "adressId")
private Adresse adresse;
EJB
#Stateless
public class UserPr {
#PersistenceContext(unitName = "proj")
private EntityManager em;
public void createUser(User user) throws RuntimeException{
try {
em.persist(user);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public User getUserById(BigInteger UserId) throws Exception{
try{
return em.createNamedQuery("User.findById", User.class).setParameter("id", UserId).getSingleResult();
}catch(Exception e){
e.printStackTrace();
}
return null;
}
}
#NamedQueries({
#NamedQuery(name="User.findById", query="SELECT u FROM User u WHERE u.id = :id")
})
public class User implements Serializable {
Try changing the way you get and set the userId.
instead of this line
session.setAttribute("UserId", user.getId());
try this
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
Map<String, Object> sessionMap = externalContext.getSessionMap();
sessionMap.put("UserId", user.getId());
and instead of this
BigInteger userId = (BigInteger) session.getAttribute("UserId");
try this
BigInteger userId = (BigInteger) sessionMap.get("UserId");
First some remarks about the mode: I don't think School should have a OneToOne relationship with User. More likely it is a OneToMany relationship. And maybe you don't want to have a separate Entity for Adresse, it could probably be an Embeddable instead.
How is your persistence set up? How are you managing transactions? The problem you see might be caused by transactional boundaries. How are the createUser and createSchool methods called exactly?

In ActiveAndroid Is it possible to update a specific field in a table row without wiping out the rest of the data in the row

ActiveAndroid:
I need to push updates to specific table fields without wiping out any existing data in the row if i dont happen to save() data for all fields each time.
For example:
I have a table named 'user' which contains the following fields:
extSrc | extId | email | firstName | lastName | role | photo | userId | verified
If i save data to all fields except 'verified' and then I later need to update only the verified field - is this possible?
Currently when i use the code below, all fields except userId (which i have stored locally) and verified are populated, all other fields are cleared. At the time that i have the verified value that i need to update i dont have the additional user data to push / save() so i only need to update the specific 'verified' field when the userId matches a userId in the user table and leave all other fields for that user as they are.
My code:
Model:
// define table name
#Table(name = "User")
public class User extends Model
{
// define table columns
#Column(name = "extSrc")
public String extSrc;
#Column(name = "extId")
public String extId;
#Column(name = "email")
public String email;
#Column(name = "firstName")
public String firstName;
#Column(name = "lastName")
public String lastName;
#Column(name = "role")
public String role;
#Column(name = "photo")
public String photo;
#Column(name = "userId", unique = true, onUniqueConflict = Column.ConflictAction.REPLACE)
public String userId;
#Column(name = "verified")
public String verified;
}
controller:
public class UserAdapter implements JsonDeserializer {
#Override
public User deserialize(JsonElement arg0, Type arg1,
JsonDeserializationContext arg2) throws JsonParseException {
User u = new User();
Log.v("user", u.toString());
JsonObject j = (JsonObject) arg0;
Log.v("j", j.toString());
if(j.has("extSrc"))
{
u.extSrc = j.get("extSrc").getAsString();
Log.v("extSrc", u.extSrc);
}
else
{
Log.v("extSrc", "does not exist");
}
if(j.has("extId"))
{
u.extId = j.get("extId").getAsString();
Log.v("extId", u.extId);
}
else
{
Log.v("extId", "does not exist");
}
if(j.has("email"))
{
u.email = j.get("email").getAsString();
Log.v("email", u.email);
}
else
{
Log.v("email", "does not exist");
}
if(j.has("firstName"))
{
u.firstName = j.get("firstName").getAsString();
Log.v("firstName", u.firstName);
}
else
{
Log.v("firstName", "does not exist");
}
if(j.has("lastName"))
{
u.lastName = j.get("lastName").getAsString();
Log.v("lastName", u.lastName);
}
else
{
Log.v("lastName", "does not exist");
}
if(j.has("role"))
{
u.role = j.get("role").getAsString();
Log.v("role", u.role);
}
else
{
Log.v("role", "does not exist");
}
if(j.has("photo"))
{
u.photo = j.get("photo").getAsString();
Log.v("photo", u.photo);
}
else
{
Log.v("photo", "does not exist");
}
if(j.has("userId"))
{
u.userId = j.get("userId").getAsString();
Log.v("userId", u.userId);
}
else
{
Log.v("userId", "does not exist");
}
if(j.has("verified"))
{
u.userId = SpontlyApplication.PREFS_USER_ID;
u.verified = j.get("verified").getAsString();
Log.v("verified", u.verified);
}
else
{
u.verified = "true";
Log.v("verified", "does not exist");
}
u.save();
}
Thanks.
If you want to update already existing user in your database, you need to retrieve it first from the database and update its fields before calling save();
Something like:
User user = new Select().from(User.class).where("userId = ?", userIdYouWantToRetrieve).executeSingle();
if (user != null){
user.setVerified(true);
} else {
user = new User(){//populate your new user here from json }
}
user.save();
This will keep the users values as were previously stored in the database and you will only update the new values you want.

Categories

Resources