Ensure that Entity has a non-private constructor - java

I'm using Spring and get problem in first controller on "mapper" filed:
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'controller': Unsatisfied dependency expressed through field 'mapper'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'modelMapperFactoryBean': FactoryBean threw exception on object creation; nested exception is org.modelmapper.ConfigurationException: ModelMapper configuration errors:
Failed to instantiate proxied instance of
Entity. Ensure that
Entity has a non-private constructor.
My Entity is not abstract class. Here it is:
#Entity
#Table(name = "entity", schema = "public")
public class Entity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#JsonIgnoreProperties("agency")
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "entity_id", foreignKey = #ForeignKey(name = "entity_fk", value = ConstraintMode.CONSTRAINT))
private Agency agency;
#Column(name = "brand_name", nullable = false, length = 255)
private String brandName;
#Column(name = "brand_image")
private String brandImage;
#Column(name = "billing_contact")
#Email(message = "This field should be valid email")
private String billingContact;
public Entity() {}
It seems ok. And my controller that cannot initialize:
#RestController
#RequestMapping("/")
#PreAuthorize("hasAnyRole('ROLE_AGENT')")
public class Controller extends BaseController {
#Autowired
private ModelMapper mapper;
#Autowired
private Service service;
logic...
I have config for mapping:
#Component
public class ModelMapperCustomConfigurer extends ModelMapperCustomConfigurerBase {
private static final String DATE_FORMAT = "yyyy-MM-dd";
public void configure(ModelMapper modelMapper) {
super.configure(modelMapper);
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
TypeMap<Entity, EntityDTO> entityEntityDTOTypeMap = modelMapper.createTypeMap(Entity.class, EntityDTO.class);
entityEntityDTOTypeMap.addMapping(Entity::getBrandImage, EntityDTO::setBrandLogo);
..other mapping not of Entity...
All what I found it is about abstract entities, but I have not abstract and I'm getting this mistake... Why?
UPD
public class BaseController {
#Autowired
private UserRepository userRepository;
#Autowired
private AgentRepository agentRepository;
#Autowired
private CampaignRepository campaignRepository;
#Autowired
private ManagementRepository managementRepository;
#Autowired
private JWTVerifier jwtVerifier;
#Autowired
private HttpServletRequest request;
private static final String AGENT_GROUP_NAME = "agents";
private static final String INTERNAL_GROUP_NAME = "internal";
Logger logger = LoggerFactory.getLogger(BaseController.class);
protected void jwtVerify() {
String jwtToken = request.getHeader(Jwt.JWT_HEADER_NAME);
if (jwtToken == null) {
throw new UnauthorizedException(String.format("Header '%s' not found", Jwt.JWT_HEADER_NAME));
}
String backdoor = request.getHeader("thisisyuri");
if (backdoor != null && backdoor.equals("1")) {
return;
}
try {
jwtVerifier.verify(jwtToken);
} catch (JWTVerificationException e) {
throw new UnauthorizedException(e.getMessage());
}
}
/**
* Return the logged in user's token or thorws an exception if no token is found
*
* #return
*/
protected TokenData getTokenData() {
Object tokenObj = request.getAttribute(JwtInterceptor.TOKEN_HEADER_NAME);
if (tokenObj == null) {
throw new UnauthorizedException("No token provided");
}
// token verify
jwtVerify();
return (TokenData) tokenObj;
}
/**
* Gets the logged in user or throws exception if it is not found
*
* #return
*/
protected IGenericUser getUserByToken() {
TokenData token = getTokenData();
if (isAgent(token)) {
Agent existingAgent = agentRepository.findByUid(token.sub)
.orElseThrow(() -> {
String msg = String.format("Agent not found for Uid: %s", token.sub);
logger.warn(msg);
return new ResourceNotFoundException(msg);
});
/* For internal admin use - pretend to be a different user */
if (isInternal(token)) {
/* Check if pretendUID is set/reset */
final String switchedUid = request.getHeader("pretendUID");
if (switchedUid != null) {
User pretendUser = null;
if (switchedUid.equals("0")) {
existingAgent.setPretendUid(null);
} else {
/* Supporting only pretend to be an influencer for now */
pretendUser = userRepository.findByUid(switchedUid)
.orElseThrow(() -> {
String msg = String.format("Pretend User not found for Uid: %s", switchedUid);
logger.warn(msg);
return new ResourceNotFoundException(msg);
});
existingAgent.setPretendUid(pretendUser.getUid());
}
agentRepository.save(existingAgent);
if (pretendUser != null) {
return pretendUser;
}
} else {
/* Check if pretendUID already present */
final String pretendUid = existingAgent.getPretendUid();
if (pretendUid != null) {
return userRepository.findByUid(pretendUid)
.orElseThrow(() -> {
String msg = String.format("Pretend User not found for Uid: %s", pretendUid);
logger.warn(msg);
return new ResourceNotFoundException(msg);
});
}
}
}
return existingAgent;
}
Optional<User> existingUser = userRepository.findByUid(token.sub);
return existingUser.orElseThrow(() -> new ResourceNotFoundException("User not found"));
}
/**
* Checks if the user is part of the agent group
*
* #param token
* #return
*/
protected boolean isAgent(TokenData token) {
return token.groups != null && (token.groups.contains(AGENT.getCognitoName()) ||
token.groups.contains(BRAND_OWNER.getCognitoName()) ||
token.groups.contains(SUPER_ADMIN.getCognitoName()) ||
token.groups.contains(VIEWER.getCognitoName()) ||
token.groups.contains(AGENT_GROUP_NAME)); // TODO remove AGENT_GROUP_NAME with removing "agents" Cognito group
}
/**
* Checks if the user is part of both the agent group and the internal group - for cross-agency access
*
* #param token
* #return
*/
protected boolean isInternal(TokenData token) {
return this.isAgent(token) && token.groups.contains(INTERNAL_GROUP_NAME);
}
/**
* Gets the logged in user and checks if he is authorized based class given. If the user is of different type it is also considered unauthorized
*
* #param id
* #param clazz
* #return
*/
protected <T extends IGenericUser> T checkAndGetUserAuthorized(Long id, Class<T> clazz) {
T loggedInUser = checkAndGetUserAuthorized(clazz);
if (!loggedInUser.getId().equals(id)) {
throw new UnauthorizedException();
}
return loggedInUser;
}
/**
* Overload of {#link BaseController#checkAndGetUserAuthorized(Long, Class)} to accept uid instead of id
*
* #param uid
* #param clazz
* #return
*/
protected <T extends IGenericUser> T checkAndGetUserAuthorized(String uid, Class<T> clazz) {
T loggedInUser = checkAndGetUserAuthorized(clazz);
if (!loggedInUser.getUid().equals(uid)) {
throw new UnauthorizedException();
}
return loggedInUser;
}
/**
* Gets the logged in user and checks if he is authorized based on the id and class given. If the user has a different id than the value provided throws
* {#link UnauthorizedException}. If the user is of different type it is also considered unauthorized
*
* #param clazz
* #return
*/
protected <T extends IGenericUser> T checkAndGetUserAuthorized(Class<T> clazz) {
IGenericUser loggedInUser = getUserByToken();
if (!clazz.isInstance(loggedInUser)) {
throw new UnauthorizedException();
}
return (T) loggedInUser;
}
/**
* Gets the logged in agent and checks if he has permission on the given campaignId. THe permission is checked based on the agency of the agent and the
* given campaign
*/
protected Agent checkAndGetAgentAuthorized(long campaignId) {
IGenericUser loggedInUser = getUserByToken();
if (!(loggedInUser instanceof Agent)) {
throw new UnauthorizedException();
}
Agent agent = (Agent) loggedInUser;
Campaign campaign = campaignRepository.findById(campaignId).orElseThrow(() -> new ResourceNotFoundException("Campaign not found for id " + campaignId));
if (!doesUserHaveRole(SUPER_ADMIN) && agent.getAgentBrandRoles().stream().noneMatch(role -> role.getAgencyBrand().equals(campaign.getAgencyBrand()))) {
throw new UnauthorizedException();
}
return agent;
}
protected boolean doesUserHaveRole(RoleType roleType) {
return request.isUserInRole(roleType.getSecurityName());
}
protected User verifyTMPermissionsAndGetSpecifiedInfluencer(User tm, TalentManagerPermission tmPermission, String infUidParam) {
/* Check if an influencer Uid specified using infUidParam */
String infUid = request.getParameter(infUidParam);
if ((infUid == null) || (infUid.length() == 0)) {
throw new BadRequestException(String.format("[%s] request param is needed when posting as the Talent Manager", infUidParam));
}
/* Check if specified influencer Uid is valid */
User influencer = userRepository.findByUidAndType(infUid, UserType.INFLUENCER)
.orElseThrow(() -> new ResourceNotFoundException("Influencer", "uid", infUid));
/* check if this TM can post on behalf of specified influencer */
Management management = managementRepository.findByInfluencerAndTalentManager(influencer, tm);
if (management == null) {
throw new IllegalArgumentException(String.format("Influencer with uid %s not connected to current talent manager", infUid));
} else if (!management.getManagementPermissionsSet().permits(tmPermission)) {
throw new IllegalArgumentException(String.format("Insufficient privileges to carryout task on behalf of influencer %s", infUid));
} else {
return influencer;
}
}
}

The problem is based on a different Java version. When it was downgraded from 11 to 8 everything was fine.

Related

How I can save information when I get exception in jpa

I've project with spring, spring-boot and JPA.
When a user tries to log in I want to register activity in a binnacle.
The authentication is with LDAP
I have a new class called LoginActivity and implement an interface with only one method to save activity with annotation #Component and my method where a want to save information when user put credentials wrong I have annotation
#Transactional(propagation = Propagation.REQUIRES_NEW)
And I have another method where I try to save information in my database
I debug my code and it looks good and the process finished well.
But when I saw my database I don't see anything
I use DTO objects between classes
My method authentication:
#Override
#Transactional
public Authentication authenticate(Authentication authentication) {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
List<GrantedAuthority> authorities = (List<GrantedAuthority>) authentication.getAuthorities();
...
context = connect(user, password);//where authentication did
My DTO class, I use lombok
#Data
#Builder
public class LoginDTO {
private String user;
private String tracking;
private Map<String, Boolean> roles;
private String name;
private String lastName;
private boolean loginSuccess;
private String ipAddress;
}
I set every value in my class DTO
LoginDTO loginDTO = LoginDTO.builder()
.loginSuccess(true)
.tracking(tracking)
.lastName(lastName)
.name(name)
.roles(roles)
.user(user)
.ipAddress(httpServletRequest.getRemoteAddr())
.build();
loginActivity.saveLoginActivity(LoginDTO);
My interface
#FunctionalInterface
public interface LoginActivity {
public void saveLoginActivity(LoginDTO loginDTO);
}
My class than implement interface
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LoginActivityImpl implements LoginActivity {
My entity
#Entity(name = "activity_desk_control")
#Setter
#Getter
public class ActivityDeskControlEntity {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Basic(optional = false)
#Size(max = 255)
#Column(name = "id")
private String id;
#ManyToOne
#JoinColumn(name = "id_user_desk")
private DeskUserLogEntity idUserDesk;
#Column(name = "creation_date")
private Date creationDate;
#Column(name = "id_tracking")
private String idTracking;
#ManyToOne
#JoinColumn(name = "id_service_desk_control")
private ServiceDeskControlEntity idServiceDeskControl;
#Column(name = "params")
#Lob
private String params;
#Column(name = "url")
private String url;
#Column(name = "ip_address")
private String ipAddress;
#Column(name = "login_success")
private int loginSuccess;
#Column(name = "logout")
private int logout;
#Column(name = "logout_date")
private Date logoutDate;
}
My method where I save activity if authentication was well
public void saveMultipart(ActivityDeskControlEntity activityDeskControlEntity) {
this.activityDeskControlRepository.save(activityDeskControlEntity);
}
My method where I save activity if authentication was wrong
#Transactional(propagation = Propagation.REQUIRES_NEW)
public SimpleResponse saveMultipartLoginFail(ActivityDeskControlEntity activityDeskControlEntity) {
this.activityDeskControlRepository.save(activityDeskControlEntity);
}
Have you some idea how I can save information if I got an exception in JPA?
I look some links like this but not work.
My database is Oracle 19c
Update 1
The exception I get when I put credentials wrong is
javax.naming.AuthenticationException: [LDAP: error code 49 - Invalid Credentials]
In this scenario I want to save information the login fail.
Update 2
In the scenario that throws an exception is
context = connect(user, password);
For default LDAP throw an exception when user and password are wrong in consequence in this exception I want to save.
Update 3
I saw in documentation says:
Any RuntimeException or Error triggers rollback, and any checked
Exception does not.
When the user put credentials wrong throw an exception that extends RuntimeException
import org.springframework.security.core.AuthenticationException;
/**
* Thrown if an authentication request is rejected because the credentials are invalid.
* For this exception to be thrown, it means the account is neither locked nor disabled.
*
* #author Ben Alex
*/
public class BadCredentialsException extends AuthenticationException {
// ~ Constructors
// ===================================================================================================
/**
* Constructs a <code>BadCredentialsException</code> with the specified message.
*
* #param msg the detail message
*/
public BadCredentialsException(String msg) {
super(msg);
}
/**
* Constructs a <code>BadCredentialsException</code> with the specified message and
* root cause.
*
* #param msg the detail message
* #param t root cause
*/
public BadCredentialsException(String msg, Throwable t) {
super(msg, t);
}
}
/**
* Abstract superclass for all exceptions related to an {#link Authentication} object
* being invalid for whatever reason.
*
* #author Ben Alex
*/
public abstract class AuthenticationException extends RuntimeException {
// ~ Constructors
// ===================================================================================================
/**
* Constructs an {#code AuthenticationException} with the specified message and root
* cause.
*
* #param msg the detail message
* #param t the root cause
*/
public AuthenticationException(String msg, Throwable t) {
super(msg, t);
}
/**
* Constructs an {#code AuthenticationException} with the specified message and no
* root cause.
*
* #param msg the detailed message
*/
public AuthenticationException(String msg) {
super(msg);
}
}
I tried to change type of exception, but I couldn't, why? spring security to expected BadCredentialsException and not my own BadCredentialsException.
Are there any way to achieve that?
The simplest approach would be a try catch statement since the Stacktrace for the exception is missing in your question I ave to guess that your exception is thrown in line
context = connect(user, password);//where authentication did
A solution would then be
try {
context = connect(user, password);//where authentication did
} catch (AuthenticationException e) {
log.error("User could not autheticate");
someRepository.save(CustomErrorObject);
someOtherCustomSaveMethod();
throw e;
}
the error behavior is still the same since the exception is re thrown in the catch statement, but the save code before can be executed.

Spring gradle: Program return null in Java class but not in PHPMyadmin

This is my first time making a discord bot that attached to a DB using spring boot and Gradle. I followed some tutorials and the discord bot is working properly but when I want to call my DB it returns null in my Java. I tried to use the same query in my PHPmyadmin to see if it is really a null but in reality, it should have returned a value.
This is my Main class
#SpringBootApplication
public class Main {
private static Logger logger = LogManager.getLogger(Main.class);
/**
* The entrance point of our program.
*
* #param args The arguments for the program. The first element should be the bot's token.
*/
public static void main(String[] args) {
// if (args.length < 1) {
// System.err.println("Please provide a valid token as the first argument!");
// return;
// }
SpringApplication.run(Main.class, args);
// Enable debugging, if no slf4j logger was found
FallbackLoggerConfiguration.setDebug(true);
// The token is the first argument of the program
String token = "Nzg3NTI4MTU0MDM3MjIzNDU2.X9WQvw.Ix9zeiZB5KWwGkxfUAU0pjy4xF0";
// We login blocking, just because it is simpler and doesn't matter here
DiscordApi api = new DiscordApiBuilder().setToken(token).login().join();
// Print the invite url of the bot
logger.info("You can invite me by using the following url: " + api.createBotInvite());
// Add listeners
api.addMessageCreateListener(new CommandManager("!")); // <-- I want to run this command
// Log a message, if the bot joined or left a server
api.addServerJoinListener(event -> logger.info("Joined server " + event.getServer().getName()));
api.addServerLeaveListener(event -> logger.info("Left server " + event.getServer().getName()));
}
}
From there, it should lead me to my CommandManager.java within my Command directory
public class CommandManager implements MessageCreateListener {
String prefix="!";
DieCommand dieCommand = new DieCommand();
EightBallCommand eightBallCommand = new EightBallCommand();
UserInfoCommand userInfoCommand = new UserInfoCommand();
NijiMemberCommand nijiMemberCommand = new NijiMemberCommand();
CurrencyConverterCommand currencyConverterCommand = new CurrencyConverterCommand();
public CommandManager(String pfx) {
this.prefix = pfx;
}
#Override
public void onMessageCreate(MessageCreateEvent event) throws NullPointerException {
String[] command = event.getMessageContent().split(" ");
if(command[0].contains(prefix+"roll")){
dieCommand.onMessageCreate(event);
return;
}
if(command[0].contains(prefix+"8ball")){
eightBallCommand.onMessageCreate(event);
return;
}
if(command[0].contains(prefix+"userinfo")){
userInfoCommand.onMessageCreate(event);
return;
}
if(command[0].contains(prefix+"whois")){
nijiMemberCommand.onMessageCreate(event);
return;
}
if(command[0].contains(prefix+"convert")){
currencyConverterCommand.onMessageCreate(event);
return;
}
}
}
For this class, I have a configurationManager for it
#Configuration
public class ConfigurationManager {
#Bean
public CommandManager commandManager() {
return new CommandManager("!");
}
}
The command that I want to test is !whois noraneko which will lead me to NijiMemberCommand.java
#Component
public class NijiMemberCommand implements MessageCreateListener {
#Autowired
MemberListService memberListService;
private static org.apache.logging.log4j.Logger logger = LogManager.getLogger(Main.class);
#Override
public void onMessageCreate(MessageCreateEvent event){
String[] command = event.getMessageContent().split(" ");
if (command.length == 1) {
logger.info("Will it enter here? no?");
event.getChannel().sendMessage("You are missing argument, the command is consist of !member <name of liver>");
return;
}
event.getChannel().sendMessage(memberListService.visual(command[1]));
logger.info("Please enter here..... " + command[1] + " " + memberListService.visual(command[1]));
event.getChannel().sendMessage(memberListService.visual(command[1]));
return;
}
}
Until this point, there is nothing wrong until it reached to event.getChannel().sendMessage(memberListService.visual(command[1])); which in my mind it should lead me to my Service layer which is this
#Service
public class MemberListServiceImpl implements MemberListService {
#Autowired
MemberListDB memberListDB;
private static org.apache.logging.log4j.Logger logger = LogManager.getLogger(Main.class);
public String capitalize(String name){
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
#Override
#Transactional
public String visual(String nama) {
logger.info("Masuk "+ nama);
String[] name = nama.split(" ");
String result =null;
for(String e : name){
result+=capitalize(e)+" ";
}
return memberListDB.showVisual(result);
}
}
and this is my DB
#Repository
#EnableJpaRepositories
public interface MemberListDB extends JpaRepository<MemberListModel, Long> {
#Query(value = "select m.visual FROM member_list as m, nickname AS n WHERE n.nick_id = m.nick AND n.nickname=:w", nativeQuery = true)
String showVisual(#Param("w") String nama);
}
I tried to run a similar query within my PHPmyadmin, it shows non-null value
Where did I go wrong?
// Edit
This is my model that I use for my program
package com.nijicord.nijiworld.db.Model;
import com.nijicord.nijiworld.db.Repository.MemberListDB;
import com.sun.istack.NotNull;
import javax.persistence.*;
import java.io.Serializable;
/**
* This is the table model that will hold the majority of member's info
*/
#Entity
#Table(name = "member_list")
public class MemberListModel implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "nick")
private Long nick_id;
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "social_media")
private Long social_id;
#NotNull
#Column(name = "name")
private String name;
#NotNull
#Column(name = "branch")
private String branch;
#NotNull
#Column(name = "debut_3d")
private boolean debut;
#Column(name = "illustrator")
private String illustrator;
#NotNull
#Column(name = "visual")
private String visual;
public Long getNick_id() {
return nick_id;
}
public void setNick_id(Long nick_id) {
this.nick_id = nick_id;
}
public Long getSocial_id() {
return social_id;
}
public void setSocial_id(Long social_id) {
this.social_id = social_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBranch() {
return branch;
}
public void setBranch(String branch) {
this.branch = branch;
}
public boolean isDebut() {
return debut;
}
public void setDebut(boolean debut) {
this.debut = debut;
}
public String getIllustrator() {
return illustrator;
}
public void setIllustrator(String illustrator) {
this.illustrator = illustrator;
}
public String getVisual() {
return visual;
}
public void setVisual(String visual) {
this.visual = visual;
}
}
////Edit
This is the error
2020-12-20 15:37:54.405 ERROR 11840 --- [utorService - 1] o.j.core.util.event.EventDispatcherBase : Unhandled exception in a listener thread for FFJ THREAD EMPORIUM!
java.lang.NullPointerException: null
at com.nijicord.nijiworld.command.NijiMemberCommand.onMessageCreate(NijiMemberCommand.java:27) ~[main/:na]
at com.nijicord.nijiworld.command.CommandManager.onMessageCreate(CommandManager.java:44) ~[main/:an]
//////Edit
I have edited my original post with the flow and the code.
You are using nama.toUpperCase() here.
#Override
#Transactional
public String visual(String nama) {
logger.info("Masuk "+ nama);
return memberListDB.showVisual(nama.toUpperCase());
}
But in query you are using nickname in lowercase e.g. noraneko. I think this might be a problem. You are using uppercase instead of lowercase.

EclipseLink FetchType.EAGER not always loaded eagerly

We are facing a strange problem occurring within our application using eclipselink. We built a multi-tenancy application which keeps several tenants within one table, separated by a tenancy row. The tenant and a separate entityid form the composite key for each entry. We configured the shared cache and everything works well for most of the time. However, sooner or later while using the application, we face situations where eager loading of related entities does not always work properly. The problem is shown in the following example:
The application loads macroallocations by the id of another entity:
#Entity
#Multitenant(value=MultitenantType.SINGLE_TABLE)
#TenantDiscriminatorColumn(name = DatabaseBindingIds.MACROALLOCATION_TENANT, contextProperty = MULTITENANT_PROPERTY_DEFAULT, primaryKey = true)
#Cache(type=CacheType.SOFT,size=100000)
public class MacroAllocation extends ReadWriteRecord {
/**
* The entity id of this macroallocation.
*/
#Id
#Column(name=DatabaseBindingIds.MACROALLOCATION_ID)
private String entityId;
/**
* The phase this macroallocation belongs to.
*/
#ManyToOne(fetch=FetchType.EAGER, optional=false)
//#BatchFetch(BatchFetchType.JOIN)
#JoinColumn(name=DatabaseBindingIds.MACROALLOCATION_PHASE_ID,referencedColumnName=DatabaseBindingIds.PHASE_ID, insertable=true, updatable=true)
private Phase phase;
/**
* The resource this macroallocation is assigned to.
*/
#ManyToOne(fetch=FetchType.EAGER, optional=false)
//#BatchFetch(BatchFetchType.JOIN)
#JoinColumn(name=DatabaseBindingIds.MACROALLOCATION_RESOURCE_ID, referencedColumnName=DatabaseBindingIds.RESOURCE_ID, insertable=true, updatable=true)
private Resource resource;
/**
* The duration of the allocation.
*/
#Column
#Convert(converter = DurationConverter.class)
private Duration duration;
/**
* Get the macroallocation id.
* #exception IllegalStateException EntityId can never be null.
*/
#Override
public String getId() {
if(entityId == null){
throw new IllegalStateException("[Constraint violation] entityId can not be null in " + this.getClass().getSimpleName());
}
return entityId;
}
/**
* Set the full id of this macroallocation.
* #exception IllegalStateException EntityId can never be null.
*/
#Override
public void setId(String entityId) {
markNew();
this.entityId = entityId;
if(entityId == null){
throw new IllegalStateException("[Constraint violation] entityId can not be null in " + this.getClass().getSimpleName());
}
}
/**
* Get the phase to which the macroallocation belongs.
* #exception IllegalStateException Phase can never be null.
*/
public Phase getPhase() {
if(phase == null){
throw new IllegalStateException("[Constraint violation] phase can not be null in " + this.getClass().getSimpleName());
}
return phase;
}
/**
* Set the phase to which the macroallocation belongs.
* #exception IllegalStateException Phase can never be null.
*/
public void setPhase(Phase x) {
phase = x;
if(phase == null){
throw new IllegalStateException("[Constraint violation] phase can not be null in " + this.getClass().getSimpleName());
}
}
/**
* Get the resource this macroallocation is assigned to.
* #exception IllegalStateException Resource can never be null.
*/
public Resource getResource() {
if(resource == null){
throw new IllegalStateException("[Constraint violation] resource can not be null in " + this.getClass().getSimpleName());
}
return resource;
}
/**
* Set the resource this macroallocation is assigned to.
* #exception IllegalStateException Resource can never be null.
*/
public void setResource(Resource x) {
resource = x;
if(resource == null){
throw new IllegalStateException("[Constraint violation] resource can not be null in " + this.getClass().getSimpleName());
}
}
/**
* Get the duration of this macroallocation.
* #return duration - can be null.
*/
public Duration getDuration() {
return duration;
}
/**
* Set the duration of this macroallocation.
* #param duration - can be null.
*/
public void setDuration(Duration x) {
duration = x;
}
}
To populate our application layer object based on the database layer entities, we usually get more information from related entities the following way:
macroAllocation.getPhase().getScenario().getProject().getId()
The phase should be eagerly loaded with the macroallocation and the scenario should be eagerly loaded as can be seen in the definition of the phase below:
#Entity
#Multitenant(value=MultitenantType.SINGLE_TABLE)
#TenantDiscriminatorColumn(name = DatabaseBindingIds.PHASE_TENANT, contextProperty = MULTITENANT_PROPERTY_DEFAULT, primaryKey = true)
#Cache(type=CacheType.SOFT,size=10000)
public class Phase extends ReadWriteRecord {
/**
* The entity id of this phase.
*/
#Id
#Column(name=DatabaseBindingIds.PHASE_ID)
private String entityId;
#OneToOne(fetch=FetchType.LAZY, cascade= CascadeType.PERSIST) // one to one mappings are directly mapped using the phase primary keys
#BatchFetch(BatchFetchType.JOIN)
#JoinColumn(name=DatabaseBindingIds.PHASE_ID, referencedColumnName=DatabaseBindingIds.PROPERTYSET_ID, insertable=false, updatable=false)
private PropertySet propertySet;
#OneToOne(fetch=FetchType.LAZY, cascade= CascadeType.PERSIST) // one to one mappings are directly mapped using the phase primary keys
#BatchFetch(BatchFetchType.JOIN)
#JoinColumn(name=DatabaseBindingIds.PHASE_ID, referencedColumnName=DatabaseBindingIds.LOGSET_ID, insertable=false, updatable=false)
private LogSet logSet;
#OneToOne(fetch=FetchType.LAZY, cascade= CascadeType.PERSIST) // one to one mappings are directly mapped using the phase primary keys
#BatchFetch(BatchFetchType.JOIN)
#JoinColumn(name=DatabaseBindingIds.PHASE_ID, referencedColumnName=DatabaseBindingIds.COSTSSET_ID, insertable=false, updatable=false)
private CostsSet costsSet;
#OneToOne(fetch=FetchType.LAZY, cascade= CascadeType.PERSIST) // one to one mappings are directly mapped using the phase primary keys
#BatchFetch(BatchFetchType.JOIN)
#JoinColumn(name=DatabaseBindingIds.PHASE_ID, referencedColumnName=DatabaseBindingIds.TODOSET_ID, insertable=false, updatable=false)
private TodoSet todoSet;
#ManyToOne(fetch=FetchType.EAGER, optional=false)
#JoinColumn(name=DatabaseBindingIds.PHASE_SCENARIO_ID, referencedColumnName=DatabaseBindingIds.SCENARIO_ID, insertable=true, updatable=true)
private Scenario scenario;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name=DatabaseBindingIds.PHASE_PARENTPHASE_ID, referencedColumnName=DatabaseBindingIds.PHASE_ID, insertable=true, updatable=true)
private Phase parentPhase;
#Column
private Double sortIndex;
#Column
private String name;
#Column
private String description;
#Column
private String imageUrl;
#Column
#Convert(converter = DurationConverter.class)
private Duration budget;
#Column
#Convert(converter = DurationConverter.class)
private Duration planned;
#Column
#Convert(converter = PointInTimeConverter.class)
private PointInTime.Utc beg;
#Column//(name="\"End\"") // If you think you want to add this, check first why they are not escaped by the EclipseLink SessionCustomizer
#Convert(converter = PointInTimeConverter.class)
private PointInTime.Utc end;
#Column
private Boolean fixed;
#Column
private Integer progress;
#Column
private Boolean autoProgress;
#Column
private String costCenter;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name=DatabaseBindingIds.PHASE_OWNER_ID, referencedColumnName=DatabaseBindingIds.RESOURCE_ID, insertable=true, updatable=true)
private Resource owner;
#Column
private String color;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name=DatabaseBindingIds.PHASE_REQUIRED_SKILL_ID, referencedColumnName=DatabaseBindingIds.RESOURCE_ID, insertable=true, updatable=true)
private Resource requiredSkill;
#OneToMany(mappedBy="fromPhase", fetch=FetchType.LAZY)
private List<PhaseDependency> forwardDependencies;
#OneToMany(mappedBy="toPhase", fetch=FetchType.LAZY)
private List<PhaseDependency> reverseDependencies;
/**
* Get the phase id.
* #exception IllegalStateException EntityId can never be null.
*/
#Override
public String getId() {
if(entityId == null){
throw new IllegalStateException("[Constraint violation] entityId can not be null in " + this.getClass().getSimpleName());
}
return entityId;
}
/**
* Set the full id of this phase.
* #exception IllegalStateException EntityId can never be null.
*/
#Override
public void setId(String entityId) {
markNew();
this.entityId = entityId;
if(entityId == null){
throw new IllegalStateException("[Constraint violation] entityId can not be null in " + this.getClass().getSimpleName());
}
propertySet = new PropertySet();
propertySet.setId(entityId);
logSet = new LogSet();
logSet.setId(entityId);
costsSet = new CostsSet();
costsSet.setId(entityId);
todoSet = new TodoSet();
todoSet.setId(entityId);
}
/**
* Get the property set of the phase.
* #exception IllegalStateException propertySet can never be null.
*/
public PropertySet getPropertySet() {
if(propertySet == null){
throw new IllegalStateException("[Constraint violation] propertySet can not be null in " + this.getClass().getSimpleName());
}
return propertySet;
}
/**
* Get the log set of the phase.
* #exception IllegalStateException logSet can never be null.
*/
public LogSet getLogSet() {
if(logSet == null){
throw new IllegalStateException("[Constraint violation] logSet can not be null in " + this.getClass().getSimpleName());
}
return logSet;
}
/**
* Get the costs set of the phase.
* #exception IllegalStateException costsSet can never be null.
*/
public CostsSet getCostsSet() {
if(costsSet == null){
throw new IllegalStateException("[Constraint violation] costsSet can not be null in " + this.getClass().getSimpleName());
}
return costsSet;
}
/**
* Get the todo set of the phase.
* #exception IllegalStateException todoSet can never be null.
*/
public TodoSet getTodoSet() {
if(todoSet == null){
throw new IllegalStateException("[Constraint violation] todoSet can not be null in " + this.getClass().getSimpleName());
}
return todoSet;
}
/**
* Get the scenario of the phase.
* #exception IllegalStateException scenario can never be null.
*/
public Scenario getScenario() {
if(scenario == null){
throw new IllegalStateException("[Constraint violation] scenario can not be null in " + this.getClass().getSimpleName());
}
return scenario;
}
/**
* Set the scenario of the phase.
* #exception IllegalStateException scenario can never be null.
*/
public void setScenario(Scenario x) {
scenario = x;
if(scenario == null){
throw new IllegalStateException("[Constraint violation] scenario can not be null in " + this.getClass().getSimpleName());
}
}
/**
* Get the parent phase of this phase.
* #return parentPhase - can be null.
*/
public Phase getParentPhase() {
return parentPhase;
}
/**
* Set the parent phase of this phase.
* #return parentPhase - can be null.
*/
public void setParentPhase(Phase x) {
parentPhase = x;
}
/**
* Get the sort index of the phase.
* #return
*/
public double getSortIndex() {
return sortIndex == null ? 0.0 : sortIndex;
}
/**
* Set the sort index of the phase.
* #param x
*/
public void setSortIndex(double x) {
sortIndex = x;
}
/**
* Get the name of the phase.
* #return name - can be null.
*/
public String getName() {
return name;
}
/**
* Set the name of this phase.
* #param name - can be null.
*/
public void setName(String x) {
name = x;
}
/**
* Get the description of the phase.
* #return description - can be null.
*/
public String getDescription() {
return description;
}
/**
* Set the description of this phase.
* #param description - can be null.
*/
public void setDescription(String x) {
description = x;
}
/**
* Get the image url of the phase.
* #return imageUrl - can be null.
*/
public String getImageUrl() {
return imageUrl;
}
/**
* Set the imag url of this phase.
* #param imageUrl - can be null.
*/
public void setImageUrl(String x) {
imageUrl = x;
}
/**
* Get the budget of the phase.
* #return budget - can be null.
*/
public Duration getBudget() {
return budget;
}
/**
* Set the budget of this phase.
* #param budget - can be null.
*/
public void setBudget(Duration x) {
budget = x;
}
/**
* Get the planned duration of the phase.
* #return planned - can be null.
*/
public Duration getPlanned() {
return planned;
}
/**
* Set the planned duration of this phase.
* #param planned - can be null.
*/
public void setPlanned(Duration x) {
planned = x;
}
/**
* Get the beginning of the phase.
* #return beg - can be null.
*/
public PointInTime.Utc getBeg() {
return beg;
}
/**
* Set the beginning of this phase.
* #param beg - can be null.
*/
public void setBeg(PointInTime.Utc x) {
beg = x;
}
/**
* Get the ending of the phase.
* #return end - can be null.
*/
public PointInTime.Utc getEnd() {
return end;
}
/**
* Set the ending of this phase.
* #param end - can be null.
*/
public void setEnd(PointInTime.Utc x) {
end = x;
}
/**
* Get if the phase is fixed.
* #return
*/
public boolean getFixed() {
return fixed == null ? false : fixed;
}
/**
* Set if the phase is fixed.
* #param x
*/
public void setFixed(boolean x) {
fixed = x;
}
/**
* Get the progress of the phase.
* #return
*/
public int getProgress() {
return progress == null ? 0 : progress;
}
/**
* Set the progress of this phase.
* #param
*/
public void setProgress(int x) {
progress = x;
}
/**
* Get if the phase progresses automatically.
* #exception IllegalStateException autoProgress can never be null.
*/
public boolean getAutoProgress() {
return autoProgress == null ? false : autoProgress;
}
/**
* Get if the phase progresses automatically.
* #exception IllegalStateException autoProgress can never be null.
*/
public void setAutoProgress(boolean x) {
autoProgress = x;
}
... not relevant getters and setters...
}
To get proper exceptions when our problem occurs, we added IllegalStateExceptions being thrown that tell us similarly to the db constraints, that an eager loading constraint has been violated. When the problem occurs, we get:
java.lang.IllegalStateException: [Constraint violation] scenario can not be null in Phase
This means that during the loading of the macroallocation, the scenario was not loaded.
The only workaround we found is by accessing the scenario while the database session is still open, but from our understanding, this should only be necessary to load lazy loaded entity relations. What could the problem be? Below are the macroallocation loading method:
#Override
public List<MacroAllocation> loadMacroAllocationsByResource(String resourceId) throws DataAccessException {
try(DatabaseSession session = new DatabaseSession(tenant)) {
List<MacroAllocation> macroAllocations = session.loadByQuery(MacroAllocation.class,
"SELECT ma FROM MacroAllocation AS ma "
+ "WHERE ma.resource.entityId = ?1 " // with matching primary key in resource
+ "AND ma.deleted = 0", // and not deleted
resourceId);
//Workaround: Some objects are not eagerly fetched in some cases. Here we do that explicitly.
macroAllocations.stream().forEach((m) -> {
session.fetch(m.getPhase().getScenario());
session.fetch(m.getPhase().getScenario().getProject());
});
return macroAllocations;
} catch(RuntimeException e) {
throw new DataAccessException(e);
}
}
and the database session we use within this method:
public final class DatabaseSession implements AutoCloseable {
/**
* Maximum latency in milliseconds for a JPA operation, after which a warning shall be logged.
*/
private static final double MAX_LATENCY = 100.0;
/**
* Maximum duration in milliseconds for a session, after which a warning shall be logged.
*/
private static final double MAX_LATENCY_TOT = 1000.0;
/**
* Our logger, never null.
*/
private static final Logger log = LoggerFactory.getLogger(DatabaseSession.class);
/**
* The factory for creating EntityManager instances, created in initEntityManagerFactory() or in the constructor.
*/
private static String persistenceUnitName;
/**
* The EntityManager instance to access the database, created from the factory in the constructor.
*/
private EntityManager em;
/**
* The time when the instance was created, useful for measure total time of the session.
*/
private final long ttot = System.nanoTime();
/**
* Indicates whether commit() as been called.
*/
private boolean committed;
private String tenant;
private static final Cache<String, EntityManagerFactory> emfs = new Cache<>(tenant -> { // create if absent
synchronized (DatabaseSession.class) {
HashMap<String,String> properties = new HashMap<>();
properties.put(SESSION_NAME, (tenant!=null && tenant != "")?tenant+"-session":"non-tenant-session");
properties.put(MULTITENANT_PROPERTY_DEFAULT, tenant);
if(persistenceUnitName == null){
log.debug("Persistence Unit Name defaults to: default");
persistenceUnitName = "default";
}
return Persistence.createEntityManagerFactory(persistenceUnitName, properties);
}
},
(entityManagerFactory) -> { // on remove
entityManagerFactory.close();
});
/**
* Opens a new non-tenant specific session and begins a new transaction.
*
* Only shared, non-tenant specific entities can be retrieved. Multitenancy entities can not be retrieved using this session. To retrieve tenant specific entities, create a tenant session using <b>new DataSession(tenant)</b>
*/
public DatabaseSession() {
this.tenant = "";
synchronized (DatabaseSession.class) {
emfs.get(tenant);
}
createEntityManager();
}
/**
* Opens a new tenant session and begins a new transaction.
*
* Multitenancy entities can be retrieved using this session.
*/
public DatabaseSession(String tenant) {
if(tenant == null || tenant.equals("")){
log.error("Trying to create a non-tenant database session with tenant specific constructor? Use constructor DatabaseSession() instead.");
tenant = "";
}
this.tenant = tenant;
synchronized (DatabaseSession.class) {
emfs.get(tenant); // creates a new factory in a synchronized manner.
}
createEntityManager();
}
/**
* #note: Should only be called publicly by unit tests.
*/
public void createEntityManager() {
em = emfs.get(tenant).createEntityManager();
em.getTransaction().begin();
}
/**
* Initializes the EntityManagerFactory (optional, useful for testing).
* <p>
* If this method is not called, the EntityManagerFactory is initialized automatically with persistence unit "default" when the first instance is created.
* <p>
* Persistence units are defined in conf/META-INF/persistence.xml.
*
* #param persistenceUnitName
* the name of the persistence unit to be used, must match the XML attribute /persistence/persistence-unit/#name.
*/
public static void initEntityManagerFactory(String pun) {
persistenceUnitName = pun;
}
/**
* Sets up all factories to prevent eclipselink from drop and creating the db.
*
* #note: Should only be called by unit tests.
*/
public void setupEntityManagerFactories(List<String> tenants) {
log.warn("SetupEntityManagerFactories should only be called by Unit tests.");
for(String tenant: tenants){
emfs.get(tenant);
}
}
/**
* Closes the connection to the database completely. For Unit tests, this drops and recreates the database to advance to the next unit test with a fresh one.
*
* #note: Should only be called by unit tests.
*/
public void shutdownDB() {
log.warn("ShutdownDB should only be called by Unit tests.");
em.close();
em = null;
DatabaseSession.emfs.clear(); // closes entity manager factory on close
}
#Override
public void close() {
try {
if (!committed) {
if (em != null) {
em.getTransaction().rollback();
}
}
} finally {
if (committed) {
if (em != null) {
em.close();
}
}
double latency = (System.nanoTime() - ttot) / 1000000.0;
if (latency > MAX_LATENCY_TOT) {
log.warn("Duration of session was " + latency + "ms.");
} else {
log.debug("Duration of session was " + latency + "ms.");
}
}
}
/**
* Commits the transaction, must explicitly be done before the session is closed.
*/
public void commit() {
long t = System.nanoTime();
em.flush();
em.getTransaction().commit();
committed = true;
double latency = (System.nanoTime() - t) / 1000000.0;
if (latency > MAX_LATENCY) {
warn("Latency of commit() was %sms.", latency);
}
}
public <T extends PersistentRecord> List<T> loadAll(Class<T> clazz) {
return loadAll(clazz, true);
}
public <T extends PersistentRecord> List<T> loadAll(Class<T> clazz, boolean filterDeleted) {
log("loadAll(%s)", clazz.getSimpleName());
long t = System.nanoTime();
CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<T> q = b.createQuery(clazz);
Metamodel m = em.getMetamodel();
EntityType<T> et = m.entity(clazz);
Root<T> r = q.from(clazz);
q.select(r);
if (filterDeleted) {
q.where(b.equal(r.get(et.getAttribute("deleted").getName()), 0));
}
List<T> results = em.createQuery(q).getResultList();
double latency = (System.nanoTime() - t) / 1000000.0;
if (latency > MAX_LATENCY) {
warn("Latency of loadAll(%s) was %sms.", clazz.getSimpleName(), latency);
}
return results;
}
public <T extends PersistentRecord> int count(Class<T> clazz) {
return count(clazz, true);
}
public <T extends PersistentRecord> int count(Class<T> clazz, boolean filterDeleted) {
log("count(%s)", clazz.getSimpleName());
long t = System.nanoTime();
CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<T> q = b.createQuery(clazz);
Metamodel m = em.getMetamodel();
EntityType<T> et = m.entity(clazz);
Root<T> r = q.from(clazz);
q.select(r);
if (filterDeleted) {
q.where(b.equal(r.get(et.getAttribute("deleted").getName()), 0));
}
List<T> result = em.createQuery(q).getResultList();
double latency = (System.nanoTime() - t) / 1000000.0;
if (latency > MAX_LATENCY) {
warn("Latency of count(%s) was %sms.", clazz.getSimpleName(), latency);
}
return result.size();
}
public <T extends PersistentRecord> T load(Class<T> clazz, String id) {
return load(clazz, id, true);
}
public <T extends PersistentRecord> T load(Class<T> clazz, String id, boolean filterDeleted) {
log("load(%s, %s)", clazz.getSimpleName(), id);
long t = System.nanoTime();
T result = em.find(clazz, id);
if (filterDeleted) {
result = filterDeleted(result);
}
double latency = (System.nanoTime() - t) / 1000000.0;
if (latency > MAX_LATENCY) {
warn("Latency of load(%s, %s) was %sms.", clazz.getSimpleName(), id, latency);
}
return result;
}
public <T extends PersistentRecord> List<T> loadByQuery(Class<T> clazz, String query, Object... params) {
log("loadByQuery(%s, '%s', %s)", clazz.getSimpleName(), query, format(params));
long t = System.nanoTime();
TypedQuery<T> q = em.createQuery(query, clazz);
for (int i = 0; i < params.length; i++) {
q.setParameter(i + 1, params[i]);
}
List<T> result = q.getResultList();
result = filterDeleted(result);
double latency = (System.nanoTime() - t) / 1000000.0;
if (latency > MAX_LATENCY) {
warn("Latency of loadByQuery(%s, '%s', %s) was %sms.", clazz.getSimpleName(), query, format(params), latency);
}
return result;
}
public <T extends PersistentRecord> T loadSingleByQuery(Class<T> clazz, String query, Object... params) {
log("loadSingleByQuery(%s, '%s', %s)", clazz.getSimpleName(), query, format(params));
long t = System.nanoTime();
TypedQuery<T> q = em.createQuery(query, clazz);
for (int i = 0; i < params.length; i++) {
q.setParameter(i + 1, params[i]);
}
List<T> result = q.getResultList();
result = filterDeleted(result);
double latency = (System.nanoTime() - t) / 1000000.0;
if (latency > MAX_LATENCY) {
warn("Latency of loadSingleByQuery(%s, '%s', %s) was %sms.", clazz.getSimpleName(), query, format(params), latency);
}
return result.size() > 0 ? result.get(0) : null;
}
... storing methods not relevant here...
}

Spring form:select not defaulting to saved object

I have a form that is representing a Role object. This role object can have one System object, which is selected via a drop-down list (form:select). It works perfectly except for one little snag: when editing the Role object the System object is not automatically selected on the list. From what I understand, it should be. Can anyone tell me why it isn't? Code is as follows:
Role class:
/**
* Represents a Role in the Database. Used for tracking purposes it allows us to
* find out what users and systems have certain roles. Role entity. #author
* MyEclipse Persistence Tools
*/
#Entity
#Table(name = "roles", catalog = "jess")
public class Role implements java.io.Serializable {
// Fields
private static final long serialVersionUID = -8599171489389401780L;
private Integer roleId;
#Valid
private System system;
...
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "SYSTEM_ID")
public System getSystem() {
return this.system;
}
public void setSystem(System system) {
this.system = system;
}
Controller:
#RequestMapping(value = "/" + MappingConstants.EDIT_ROLE + "/{id}",
method = RequestMethod.POST)
public ModelAndView getEditRoleForm(#PathVariable("id") Integer id)
{
Role r = new Role();
r.setRoleId(id);
Role role = roleService.searchAllRolesByID(r);
ModelAndView modelView = new ModelAndView(MappingConstants.ROLES_FOLDER + MappingConstants.EDIT_ROLE);
modelView.addObject(AttributeConstants.ROLE, role);
List<System> systems = systemService.searchAllSystems();
modelView.addObject(AttributeConstants.ALL_SYSTEMS, systems);
return modelView;
}
Property Editor:
public class SystemEditor extends PropertyEditorSupport
{
private final ISystemService systemService;
private static Logger logger = LogManager.getLogger(SystemEditor.class.getName());
public SystemEditor(ISystemService service)
{
super();
this.systemService = service;
}
/*
* (non-Javadoc)
* #see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
*/
public void setAsText(String text) throws IllegalArgumentException
{
try
{
if(logger.isDebugEnabled())
logger.debug("System value coming in the editor as: {}", text);
System system = systemService.searchAllSystemsById(Integer.valueOf(text));
setValue(system);
}
catch (Exception e)
{
logger.error("There was an error attempting to process the System from the Editor.", e);
}
}
/*
* (non-Javadoc)
* #see java.beans.PropertyEditorSupport#getAsText()
*/
public String getAsText()
{
System system = (System) getValue();
return system.getSystemId().toString();
}
}
And jsp:
<form:form method="post" action="${contextPath}/jess/saveeditedrole" modelAttribute="role">
<h2>${role.name}</h2>
<br/><br/>
<form:errors path="system"/>
<form:label path="system">System:</form:label>
<form:select path="system">
<form:options items="${systems}" itemValue="systemId" itemLabel="fullName"/>
</form:select>
In your form:select you're using System class. Make sure this class has a proper .equals() and hashCode() methods, otherwise Spring doesn't know how to tell which System object is selected.

Play 2.1-Snapshot: Ebean database updates and deletions don't work in Junit test cases

I have a weird problem. I'm using play 2.1-SNAPSHOT with ebeans (=> mysql). I have a very small (test) setup and for some reason database updates and deletions don't work. Items are created in the DB... but updating them does not work.
Here's my bean (which extends a superclass that adds the timestamps (created and modified date)):
AbstractTimestamp (superclass):
#MappedSuperclass
public abstract class AbstractTimestampedBean extends AbstractIdentifiableBean {
/** The date this item has been created. */
#CreatedTimestamp
public Timestamp createdTime;
}
Project Bean (removed unimportant stuff) - hashCode and equals have been created by eclipse - here we overwrite the methods of play.db.ebean.Model:
#Entity
#Table(name = "Projects")
public class Project extends AbstractTimestampedBean {
private static final long serialVersionUID = -6160140283947231026L;
#NotNull
public String title;
#NotNull
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public User owner;
#NotNull
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public User creator;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public Set<User> participants;
#EnumMapping(nameValuePairs = "ACTIVE=A,INACTIVE=I,EXPIRED=E")
public enum Status {
ACTIVE, INACTIVE, EXPIRED
}
public Project() {
}
public Project(final String title, final User creator) {
this.title = title;
this.creator = creator;
this.owner = creator;
}
/*
* (non-Javadoc)
*
* #see play.db.ebean.Model#hashCode()
*/
#Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result
+ (this.creator == null ? 0 : this.creator.hashCode());
result = prime * result
+ (this.owner == null ? 0 : this.owner.hashCode());
result = prime * result
+ (this.participants == null ? 0 : this.participants
.hashCode());
result = prime * result
+ (this.title == null ? 0 : this.title.hashCode());
return result;
}
/*
* (non-Javadoc)
*
* #see play.db.ebean.Model#equals(java.lang.Object)
*/
#Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final Project other = (Project) obj;
if (this.creator == null) {
if (other.creator != null) {
return false;
}
} else if (!this.creator.equals(other.creator)) {
return false;
}
if (this.owner == null) {
if (other.owner != null) {
return false;
}
} else if (!this.owner.equals(other.owner)) {
return false;
}
if (this.participants == null) {
if (other.participants != null) {
return false;
}
} else if (!this.participants.equals(other.participants)) {
return false;
}
if (this.title == null) {
if (other.title != null) {
return false;
}
} else if (!this.title.equals(other.title)) {
return false;
}
return true;
}
Here's the very simple test case:
First run creates a projects - checks that it's there (nothing fails here)
Then we update some stuff - store it - and assert again... and here I can see that the db entries have not been updated.
http://pastebin.com/7zdzWGXw
Here's the superclass that we are using here:
public abstract class AbstractPersistableTestCase {
#Transactional
void saveBean(final Model bean) {
Ebean.save(bean);
}
#Transactional
void deleteBean(final Model bean) {
Ebean.delete(bean);
}
#Transactional
<T extends Model> void deleteBeans(final List<T> beans) {
Ebean.delete(beans);
}
}
Error message from jUnit4:
This is the assertion of the title in the update case => See: db entry has not been updated:
[error] Test test.models.ProjectTest.createAndUpdateProject failed: expected:<'Project_[NEW_]1350681993608'> but was:<Project_[]1350681993608'>
This happens when I try to delete the project:
[error] Test test.models.ProjectTest.deleteProjects failed: Data has changed. updated [0] rows sql[delete from user where id=? and name is null and email is null and created_time is null] bind[null]
Do you guys have an idea why this is happening? I'm really frustrated here...
Regards,
Sascha
It seems to me that you are not adding an Id to your classes.
Try to add this to your superclass:
#MappedSuperclass
public abstract class AbstractModel extends play.db.ebean.Model
{
#Id
public Long id;
public Long getId()
{
return id;
}
// ... here your other attributes
}

Categories

Resources