Hibernate didn't create one of tables - spring

I'm learning spring..., I build a simple application.
I have a problem because hibernate does not want to generate one of the models..
I do not know what's wrong
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.io.Serializable;
#Entity
#Table(name="access_card")
public class AccessCard implements Serializable{
private static final long serialVersionUID = -4015209774835055079L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private Long id;
#Column(name="key")
private String key;
#Column(name="enabled")
private Boolean enabled;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
#JsonIgnore
private User user;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
Other classes are written similarly and everything works.
Error:
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error
executing DDL "create table access_card (id bigint not null, enabled
bit, key varchar(255), user_id bigint not null, primary key (id))
engine=MyISAM" via JDBC Statement
And properties
spring.jpa.hibernate.ddl-auto = update
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

Follow your comment, The problem pointed out at here
key varchar(255)
The table name is reserved keyword.
See https://hibernate.atlassian.net/browse/HHH-4453
If you still want it, the solution is https://vladmihalcea.com/escape-sql-reserved-keywords-jpa-hibernate/

Related

JPA lazy initialization error with #OneToMany #EmbeddedId

In Sprinboot/JPA I defined an entity with one-to-may association as follows:
#Entity
#Table(name = "useraccount", catalog = "useraccount")
public class UserAccount implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//other stuff...
#OneToMany(mappedBy ="tokenId.user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.REFRESH}, orphanRemoval =true, fetch=FetchType.LAZY)
private Set<SecureToken> tokens = new HashSet<>();
public Set<SecureToken> getTokens()
{
return this.tokens;
}
//other getter and setter
}
The SecureToken entity:
#Entity
#Table(name = "secureToken", catalog = "useraccount")
public class SecureToken implements Serializable
{
#EmbeddedId
public SecureTokenId tokenId= new SecureTokenId();
#Column(unique = true)
private String token;
private Timestamp isConsumed;
#CreationTimestamp
#Column(updatable = false)
private Timestamp timestamp;
#Column(updatable = false)
#Basic(optional = false)
private Timestamp expireAt;
#MapsId("user_id")
#JoinColumn(name = "user_id", referencedColumnName ="id")
#ManyToOne
private UserAccount user;
public SecureToken(UserAccount user, String token, String tokenType, Timestamp timestamp, Timestamp expire)
{
super();
this.token=token;
this.tokenId.setTokenType(tokenType);
this.tokenId.setUser(user);
this.timestamp=timestamp;
this.expireAt=expire;
this.isExpired=false;
}
}
The SecureTokenId:
#Embeddable
public class SecureTokenId implements Serializable
{
#Column(name="tokenType")
private String tokenType;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private UserAccount user;
public SecureTokenId()
{
super();
}
public SecureTokenId(String tokenType)
{
//this.user_id=user_id;
this.tokenType=tokenType;
}
#Override
public boolean equals(Object o)
{
if (o == null || getClass() != o.getClass())
return false;
SecureTokenId that = (SecureTokenId) o;
return Objects.equals(this.tokenType, that.tokenType) &&
Objects.equals(this.user.getId(), that.user.getId());
}
#Override
public int hashCode() {
return Objects.hash(tokenType, this.user.getId());
}
public void setTokenType(String tokenType)
{
this.tokenType=tokenType;
}
public String getTokenType()
{
return this.tokenType;
}
public void setUser(UserAccount user)
{
this.user=user;
}
public UserAccount getUser()
{
return this.user;
}
public Long getTokenId()
{
return this.user.getId();
}
}
But calling the method getToken() of entity UserAccount gets the famous "LazyInitializationException". I generally use Hibernate.initialize, but with this configuration I cannot get rid of the problem.
This how I create a token within a #Service annoted SecureTokenService class.
#Override
#Transactional
public SecureToken generateToken(UserAccount user, String tokenType)
{
byte[] random = new byte[64];
new SecureRandom().nextBytes(random);
Timestamp timestamp = java.sql.Timestamp.valueOf(LocalDateTime.now());
LocalDateTime expire= LocalDateTime.now().plusHours(12);
SecureToken token = new SecureToken(new SecureTokenId(user, tokenType),Base64.encodeBase64URLSafeString(random),
timestamp, Timestamp.valueOf(expire));
return token;
}
Then in the UserService class (#Service annotated) I try to create a token:
SecureToken token = secureTokenService.generateToken(user, type);
secureTokenService.save(token);
user.addSecureToken(token); //Error
this.save(user)
When I try to associate the token with the user the error is thrown. Without that statement, the application seems working but even with "spring.jpa.open-in-view = false" in application.properties calling user.getTokens() rises the lazy initialization error.
In parent child relationship, you didn't declare any parent reference from child side.
In the parent side (UserAccount), you declared as follows
#OneToMany(mappedBy ="user"....
Which means your child side (SecureToken) there is no such property named user.
To get rid of this situation,
First you need to declare user inside of SecureToken / SecureTokenId. From your definition, you declared user_id inside SecureTokenId, instead declare user inside SecureTokenId.
...
public class SecureTokenId ... {
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private UserAccount user;
...
}
Then in the UserAccount declare the #OneToMany as follows
#OneToMany(mappedBy ="tokenId.user"...
private Set<SecureToken> tokens;

I'm trying to implement group by on a table and want to implement it from repository

I've a table tbl_rating, I'm trying to get avg o=rating of the users that are existing in this table.
This is the table I have.
CREATE TABLE `tbl_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`email` varchar(255) NOT NULL,
`contact` varchar(255) NOT NULL,
`status` enum('active','inactive') NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
UNIQUE KEY `contact` (`contact`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1
this is the tbl_rating
CREATE TABLE `tbl_rating` (
`id` bigint(255) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL,
`point` int(1) NOT NULL,
`status` enum('active','inactive') NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1
this is the query that I'm trying to implement from repository.
SELECT r.user_id, u.name, AVG(r.point) AS average
FROM tbl_rating r LEFT JOIN tbl_user u ON u.id=r.user_id
GROUP BY r.user_id;
this is my controller
#RequestMapping(value = "/user/rating/avg/individual", method = RequestMethod.POST)
public JsonNode getAvgRatingOfIndividual(HttpServletRequest httpServletRequest) throws IOException {
JSONArray response = new JSONArray();
List<Rating> ratings = new ArrayList<>();
try {
ratings = ratingService.getAvgRatingOfIndividual();
if (ratings.size() > 0) {
} else {
response = new JSONArray();
}
} catch (Exception e) {
return objectMapper.readTree(utility.createResponse(500, KeyWord.ERROR, e.toString()).toString());
}
return objectMapper.readTree(response.toString());
}
following are the service and repository where I'm stuck
service
public List<Rating> getAvgRatingOfIndividual() {
return ratingRepository.??
}
repository
#Repository
public interface RatingRepository extends JpaRepository<Rating, Long> {
}
rating model
#Entity
#Table(name = "tbl_rating")
public class Rating {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
long id;
#ManyToOne
#JoinColumn(name = "user_id")
User user;
#Column(name = "point")
int point;
#Column(name = "status")
String status;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public int getPoint() {
return point;
}
public void setPoint(int point) {
this.point = point;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
user model
#Entity
#Table(name = "tbl_user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
long id;
#Column(name = "name")
String name;
#Column(name = "email")
String email;
#Column(name = "contact")
String contact;
#Column(name = "status")
String status;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getContact() {
return contact;
}
public void setContact(String contact) {
this.contact = contact;
}
}
would appreciate if i could get any sort of help on this native query writing in spring boot repository
You can use from a DTO and fill it implicitly in repository.
package com.test.dto;
#Value
public class RatingDTO{
private Long userId;
private String name;
private Double average;
}
And in the repository class:
//RatingRepository.java
#Repository
public interface RatingRepository extends JpaRepository<Rating, Long> {
#Query("SELECT new com.test.dto.RatingDTO(R.user.id, R.user.name, AVG(R.point)) FROM Rating R GROUP BY R.user.id")
List<RatingDTO> getAveragePoints();
}

Unable to fetch the data from the database when tables are mapped by many to many

I am unable to fetch the users data from the database, and I also wonder how to handle the rest request in many to many cases for the same scenario.
I am using Spring Boot and Spring Data JPA. My code for the database is below:
CREATE TABLE `m3_group` (
`GROUP_ID` bigint(11) NOT NULL AUTO_INCREMENT,
`GROUP_NAME` varchar(30) DEFAULT NULL,
`GROUP_CREATED_DATE` datetime DEFAULT NULL,
`GROUP_ADMIN` varchar(14) DEFAULT NULL,
PRIMARY KEY (`GROUP_ID`)
)
ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE `m3_user` (
`USER_ID` bigint(11) NOT NULL AUTO_INCREMENT,
`USER_NAME` varchar(50) DEFAULT NULL,
PRIMARY KEY (`USER_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=330 DEFAULT CHARSET=utf8;
CREATE TABLE `m3_user_group` (
`GROUP_USER_ID` bigint(11) DEFAULT NULL,
`GROUP_ID` bigint(11) DEFAULT NULL,
KEY `FK1_GROUP_ID` (`GROUP_ID`),
KEY `FK2_USER_ID` (`GROUP_USER_ID`),
CONSTRAINT `FK1_GROUP_ID` FOREIGN KEY (`GROUP_ID`) REFERENCES `m3_group` (`GROUP_ID`),
CONSTRAINT `FK2_USER_ID` FOREIGN KEY (`GROUP_USER_ID`) REFERENCES `m3_user` (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#Entity
#Table(name = "M3_USER")
public class User implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "USER_ID")
private long userId;
#NotBlank
#Column(name = "USER_NAME")
private String userName;
//many-to-many
#ManyToMany(mappedBy="listOfUsers",fetch=FetchType.EAGER)
private List<Group> listOfGroup=new ArrayList<Group>();
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public List<Group> getListOfGroup() {
return listOfGroup;
}
public void setListOfGroup(List<Group> listOfGroup) {
this.listOfGroup = listOfGroup;
}
}
#Entity
#Table(name="M3_GROUP")
public class Group implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="GROUP_ID")
private long groupId;
#Column(name="GROUP_NAME")
private String groupName;
#CreatedDate
#Temporal(TemporalType.TIMESTAMP)
#Column(name="GROUP_CREATED_DATE")
#JsonFormat(locale = "en-IN", shape = JsonFormat.Shape.STRING, pattern =
"yyyy-MM-dd HH:mm", timezone = "GMT+5:30")
private Date groupCreatedDate;
#Column(name="GROUP_ADMIN")
private String groupAdminMobileNumber;
//many-to-many
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(name = "M3_USER_GROUP", joinColumns = #JoinColumn(name =
"GROUP_USER_ID") , inverseJoinColumns = #JoinColumn(name = "GROUP_ID") )
private List<User> listOfUsers=new ArrayList<User>();
public long getGroupId() {
return groupId;
}
public void setGroupId(long groupId) {
this.groupId = groupId;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public Date getGroupCreatedDate() {
return groupCreatedDate;
}
public void setGroupCreatedDate(Date groupCreatedDate) {
this.groupCreatedDate = groupCreatedDate;
}
public String getGroupAdminMobileNumber() {
return groupAdminMobileNumber;
}
public void setGroupAdminMobileNumber(String groupAdminMobileNumber) {
this.groupAdminMobileNumber = groupAdminMobileNumber;
}
public List<User> getListOfUsers() {
return listOfUsers;
}
public void setListOfUsers(List<User> listOfUsers) {
this.listOfUsers = listOfUsers;
}
}
#Repository
public interface GroupRepository extends JpaRepository<Group, Long> {
List<Group> findByGroupId(long groupid);
}
#RestController
public class GroupController
{
#Autowired
GroupRepository groupRepository;
#RequestMapping(value="/find/{groupId}",method=RequestMethod.POST)
public ResponseEntity<List<Group>> find(#PathVariable String groupId)
{
long id=Long.parseLong(groupId);
List<Group> group = groupRepository.findByGroupId(id);
System.out.println(group.toString());
return new ResponseEntity<List<Group>>(group,HttpStatus.OK);
}
}
I have mapped the user and group table with many to many bidirectional and i am trying to fetch the data i.e users associated with the groupId but listOfUsers is showing empty.
My rest request is:
Http ://localhost:5000/find/1
And the response is:
[
{
"groupId": 1,
"groupName": "Om namo raghavendra",
"groupCreatedDate": "2017-05-17 12:48",
"groupAdminMobileNumber": "917676060664",
"listOfUsers":[
]
}
]
listOfUsers is empty, so I want to users by using groupId.

Hibernate transaction and session with multiple save

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

Spring Security + JPA user schema

As per Spring documentation if you need to manage spring security via database you should have some standard schema of tables. for example.
create table users(
username varchar(256) not null primary key,
password varchar(256) not null,
enabled boolean not null
);
create table authorities (
username varchar(256) not null,
authority varchar(256) not null,
constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);
The problem I am facing is following.
1) Not able to understand how could I achieve such schema of tables using JPA?
I have tried something as follows.
#Entity
#Table(name="USERS")
public class UsersPersistence extends Users implements Serializable{
private static final long serialVersionUID = 1009548075747154488L;
public UsersPersistence() {
super();
}
public UsersPersistence(long id, String userName, String password, boolean enabled) {
super(id, userName,password,enabled);
}
#Id
#GeneratedValue
#Column(name="id")
#Override
public long getId() {
return super.getId();
}
#Column(name="username", nullable=false)
#Override
public String getUserName() {
return super.getUserName();
}
#Column(name="password", nullable=false)
#Override
public String getPassword() {
return super.getPassword();
}
#Column(name="enabled", nullable=false)
#Override
public boolean isEnabled() {
return super.isEnabled();
}
}
This table created as per requirement stated in Spring documentation schema.
Problem in understanding is when i am trying to assign a foreign key on username in authorities table.Since JPA assign the foreign key's via the id of parent table (primary key Table) Or may be i do not know how to assign it.
Following is the JPA class which create problem :-
#Entity
#Table(name="AUTHORITIES")
public class AuthoritiesPersistence extends Authorities implements Serializable{
private static final long serialVersionUID = 1L;
public AuthoritiesPersistence() {
super();
}
public AuthoritiesPersistence(long id, UsersPersistence userName, String authority) {
super(id,userName,authority);
}
#Id
#GeneratedValue
#Column(name="id")
#Override
public long getId() {
return super.getId();
}
#Override
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="username", nullable=false)
public UsersPersistence getUserName() {
return (UsersPersistence) super.getUserName();
}
#Column(name="authority", nullable=false)
#Override
public String getAuthority() {
return super.getAuthority();
}
}
This table is created successfully but Spring security authentication is not able to recognize the username because JPA uses the foreign key id than the actual user name.
Any help would be appreciable. I am really stuck in creating a foreign key which will be based on the username rather than the id.
Thanks
You only have to stick to the schema given in Spring Security reference docs, when using a default JdbcDaoImpl as UserDetailsService implementation, which is the case if you have the <jdbc-user-service> tag in your security configuration. Even then it is possible to override the default SQL queries it uses to fetch users and authorities (refer to the namespace appendix).
However if you manage user accounts using hibernate, it would make sense to write your own UserDetailsService implementation, instead of trying to create JPA entities that result in the specific schema required by the JdbcDaoImpl.
The documentation states as well:
If your application does use an ORM tool, you might prefer to write a custom UserDetailsService to reuse the mapping files you've probably already created.
There are a couple of ways you could handle this:
You could write your own AuthenticationProvider using Hibernate for
DB access
You could use the <jdbc-user-service> and overwrite the SQL queries as mentioned by #zagyi. (http://static.springsource.org/spring-security/site/docs/current/reference/appendix-namespace.html#nsa-jdbc-user-service)
Or you can create the your schema to fit the standard <jdbc-user-service>
To use the approach you are interested in you have to know that aside from having the fields username, password and enabled spring security expects the username to be a unique identifier. This means that you can use the username property of your entity as Id for your DB and Hibernate.
If you don't want to do this a way of approaching this is to set a table wihch defines the authorites using an ID/Name and the authority. And then to set up the use a jointable to map them to the users.
Some untested examplecode:
Role:
#Entity
#DynamicUpdate
#Table(name ="authorities")
public class Authority{
private String authority;
#Id
#Column(name="authority")
public String getAuthority() {
return authority;
}
User:
#Entity
#DynamicUpdate
#Table(name = "users", uniqueConstraints={ #UniqueConstraint(columnNames={"username"})})
public class User {
private String username;
private List<Authority> authorities;
#Type(type = "numeric_boolean")
private boolean enabled;
#Id
#Column(name="username")
public String getUsername() {
return username;
}
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "authorities",
joinColumns = #JoinColumn(name = "username"),
inverseJoinColumns = #JoinColumn(name = "rolename")
)
public List<Authority> getauthorities() {
return authorities;
}
#Column(name="ENABLED")
public boolean isEnabled() {
return enabled;
}
When the base is running you can add properties for internal use as u like.
I have figure out the Alternative which is "Configuring the JdbcUserDetailsManager to use custom SQL queries" at least i can create my tables via JPA and Can hope
" users-by-username-query and authorities-by-username-query " would do my work indeed.
To achieve it I have to add following schema .
create table custom_user_authorities (
id bigint identity,
user bigint not null,
authority varchar(256) not null,
);
this schema have id(which will be auto-incremented) which will definitely work for JPA.
I was able to map those tables using the following class definition:
#Entity
#Table(name = "users")
public class User {
#Id
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column
private boolean enabled;
#Column
private String firstName;
#ElementCollection
#JoinTable(name = "authorities", joinColumns = {#JoinColumn(name = "email")})
#Column(name = "authority")
private Set<String> roles;
public User() {
}
public Serializable getId() {
return username;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setPassword(String password) {
this.password = password;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<String> getRoles() {
return roles;
}
public void setRoles(Set<String> roles) {
this.roles = roles;
}
}

Resources