i have dto and model layer.And i want convert dto to model layer like this code.How do i fix this error?I have all getter and setter i need
model/travel class
public Travel convert(TravelDTO dto) {
this.setTravelID(dto.getTravelID());
this.setTravelCost(dto.getTravelCost());
this.setTravelStart(dto.getTravelStart());
this.setTravelEnd(dto.getTravelEnd());
this.setLocation(dto.getLocation());
this.setTravelPurpose(dto.getTravelPurpose());
this.setUser(new User().convert(dto.getUser()));
return this;
}
dto/travelDTO class
public TravelDTO convert(Travel entity) {
this.setTravelID(entity.getTravelID());
this.setTravelCost(entity.getTravelCost());
this.setTravelStart(entity.getTravelStart());
this.setTravelEnd(entity.getTravelEnd());
this.setLocation(entity.getLocation());
this.setTravelPurpose(entity.getTravelPurpose());
this.setUser(new UserDTO().convert(entity.getUser()));
return this;
}
userDto / convert code
public UserDTO convert(User entity) {
this.setUserID(entity.getUserID());
this.setFirstName(entity.getFirstName());
this.setLastName(entity.getLastName());
this.setManagerId(entity.getManagerId());
this.setPassword(entity.getPassword());
this.setRegNumber(entity.getRegNumber());
this.setUserName(entity.getUserName());
this.setDepartment(new DepartmentDTO().convert(entity.getDepartment()));
this.setTravel(new TravelDTO().convert(entity.getTravel()));
return this;
}
The error message explains the issue :). It says the entity.getUser() is returning a list of users. But the method accepts one User object.
The problem is your entity.getUser() is returning List whereas your convert method of UserDTO is expecting single User model object.
As I can see from you screenshot, UserDTO.convert method accepts argument of type User, and you are trying to pass argument with type List. I guess, the possible solution is to make Travel.getUser() return User instead of List.
UPDATE
You may iterate through your list of Users, converting each one to UserDTO and then adding it to collection, then passing it as an argument to this.setUser. Something like this: `
List<UserDTO> userDTOs = new ArrayList<>();
List<User> users = entity.getUser();
for (User user : users) {
UserDTO userDTO = new UserDTO.convert(user);
userDTOs.add(userDTO);
}
this.setUser(userDTOs);
And please pay attention that your TravelDTO class has List<User> user field. I guess, it should be List<UserDTO> users.
Related
I'm having trouble converting between java.sql.Timestamp and java.time.Instant using JOOQ converters.
Here's a simplified version of the code I'm working with.
public class User {
private static final Converter<Timestamp, Instant> MY_CONVERTER= Converter.of(
Timestamp.class,
Instant.class,
t -> t == null ? null : t.toInstant(),
i -> i == null ? null : Timestamp.from(i)
)
public static Table<?> table = DSL.table("user");
public static Field<String> name = DSL.field(DSL.name(table.getName(), "name"), String.class);
public static Field<Instant> name = DSL.field(DSL.name(table.getCreated(), "created"), SQLDataType.TIMESTAMP.asConvertedDataType(Converter.of(MY_CONVERTER)));
}
private class UserDto {
private String name;
private Instant created;
// getters, setters, etc.
}
public class UserWriter {
// constructor with injected DefaultDSLContext etc..
public void create(UserDto user) {
dslContext.insertInto(User.table, User.firstName, User.lastName)
.values(user.getName(), user.getCreated())
.execute();
}
}
public class UserReader {
// constructor with injected DefaultDSLContext etc..
public Result<Record> getAll() {
return dslContext.select().from(User.table).fetch();
}
}
public class UserService {
// constructor with injected UserReader etc..
public Collection<UserDto> getAll() {
return userReader
.getAll()
.stream()
.map(Users::from)
.collect(Collectors.toList());
}
}
public class Users {
public static UserDto from(Record record) {
UserDto user = new UserDto();
user.setName(record.get(User.name));
user.setCreated(record.get(User.created);
return user;
}
}
When I create a new User the converter is called and the insertion works fine. However, when I select the Users the converter isn't called and the record.get(User.created) call in the Users::from method returns a Timestamp (and therefore fails as UserDto.setCreated expects an Instant).
Any ideas?
Thanks!
Why the converter isn't applied
From the way you phrased your question (you didn't post the exact SELECT statement that you've tried), I'm assuming you didn't pass all the column expressions explicitly. But then, how would jOOQ be able to find out what columns your table has? You declared some column expressions in some class, but that class isn't following any structure known to jOOQ. The only way to get jOOQ to fetch all known columns is to make them known to jOOQ, using code generation (see below).
You could, of course,let User extend the internal org.jooq.impl.TableImpl class and use internal API to register the Field values. But why do that manually, if you can generate this code?
Code generation
I'll repeat the main point of my previous question, which is: Please use the code generator. I've now written an entire article on why you should do this. Once jOOQ knows all of your meta data via code generation, you can just automatically select all columns like this:
UserRecord user = ctx
.selectFrom(USER)
.where(USER.ID.eq(...))
.fetchOne();
Not just that, you can also configure your data types as INSTANT using a <forcedType>, so you don't need to worry about data type conversion every time.
I cannot stress this enough, and I'm frequently surprised how many projects try to use jOOQ without code generation, which removes so much of jOOQ's power. The main reason to not use code generation is if your schema is dynamic, but since you have that User class, it obviously isn't dynamic.
My Java Spring application, which uses ACLs, has a service method to retrieve all the objects corresponding to a given user:
#Override
#PostFilter("hasPermission(filterObject, 'READ') or hasPermission(filterObject, 'ADMINISTRATION')")
public List<SomeClass> findAll() {
return SomeClassRepository.findAll();
}
Unfortunately, where there are many objects in the database, this method takes too much time to complete (over 1 second). Probably because it will first fetch all the objects from the database, and then filter them one by one in memory. How can I optimize this without losing the benefits of Spring ACLs?
Edit: the solution I came up with for now is to create repositories for the acl_sid and acl_entry repositories and to fetch the IDs of the object of interest through those repositories. This gives me a 10x improvement in execution time compared to the method above. The new code looks like this:
#Override
#PostFilter("hasPermission(filterObject, 'READ') or hasPermission(filterObject, 'ADMINISTRATION')")
public List<SomeClass> findAll() {
List<SomeClass> result = new ArrayList<SomeClass>();
Long userId = (Long) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
AclSid sid = aclSidRepository.findBySid(Long.toString(userId));
List<AclEntry> aclEntries = aclEntryRepository.findBySid(sid);
for (AclEntry aclEntry : aclEntries) {
AclObjectIdentity aclObjectIdentity = aclEntry.getAclObjectIdentity();
AclClass aclClass = aclObjectIdentity.getObjectIdClass();
if (aclClass.getClassName().equals("com.company.app.entity.SomeClass")) {
Optional<SomeClass> SomeClass = SomeClassRepository
.findById(aclObjectIdentity.getObjectIdIdentity());
if (SomeClass.isPresent()) {
result.add(SomeClass.get());
}
}
}
return result;
}
As Spring filters the information in memory the performance will depend on the actual number of results: if there is a large amount of them, I am afraid that perhaps only caching your repository results before filtering the information may be a suitable solution.
To deal with the problem, you can filter the results at the database level. Two approaches come to my mind:
Either use Specifications, and filter the results at the database level taking into account the information about the principal exposed by Spring Security SecurityContext and including the necessary filter Predicates in order to restrict the information returned.
Or, if you are using Hibernate, use entity filters to again, based on the information about the principal exposed by Spring Security, apply the necessary data restrictions. Please, see this related SO question which provides great detail about the solution.
Please, consider for instance the use case of the Spring Data Specifications.
Instead of SomeClass, let's suppose that we are working with bank accounts. Let's create the corresponding entity:
#Entity
public class BankAccount {
#Id
private String accountNumber;
private Float balance;
private String owner;
private String accountingDepartment;
//...
}
And the corresponding repository:
public interface BankAccountRepository extends Repository<BankAccount, String>, JpaSpecificationExecutor<BankAccount, String> {
}
In order to filter the information depending on the user who is performing the query, we can define an utility method that, based on the user permissions, returns a List of Predicates that we can add later to the ones we are using in a certain Specification when filtering the bank accounts:
public static List<Predicate> getPredicatesForRestrictingDataByUser(Root<BankAccount> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// I realized in your edit that you are returning the user id instead of the user object.
// There is nothing wrong with it but you are losing a valuable information: if you provide
// a convenient UserDetails implementation you can have direct access to the authorities a user has, etc
User user = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
// Restrict data based on actual permissions
// If the user is an admin, we assume that he/she can see everything, and we will no return any predicates
if (hasAuthority(user, 'ADMINISTRATION')) {
return Collections.emptyList();
}
// Let's introduce the accounting manager role.
// Suppose that an accounting manager can see all the accounts in his/her department
if (hasAuthority(user, 'ACCOUNTING_MANAGER')) {
return Collections.singletonList(cb.equal(root.get(BankAccount_.accountingDeparment), user.getDepartment()))
}
// In any other case, a user can only see the bank account if he/she is the account owner
return Collections.singletonList(cb.equal(root.get(BankAccount_.owner), user.getId()));
}
Where hasAuthority can look like:
public static boolean hasAuthority(User user, String... authorities) {
if (user instanceof UserDetails) {
for (String authority : authorities) {
return authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.findAny(a -> a.equals(authority))
.isPresent();
}
}
return false;
}
Now, use these methods when constructing your Specifications. Consider for instance:
public static Specification<BankAccount> getBankAccounts(final BankAccountFilter filterCriteria) {
return new Specification<BankAccount>() {
#Override
public Predicate toPredicate(Root<BankAccount> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<Predicate>();
// Build your predicate list according to the user provided filter criteria
String accountNumber = filterCriteria.getAccountNumber();
if (accountNumber != null) {
predicates.add(cb.equal(root.get(BankAccount_.accountNmber), accountNumber);
}
//...
// And now, restrict the information a user can see
// Ideally, define getPredicatesForRestrictingDataByUser in a generic class more suitable for being reused
List<Predicate> predicatesForRestrictingDataByUser = getPredicatesForRestrictingDataByUser(root, query, cb);
predicates.addAll(predicatesForRestrictingDataByUser);
Predicate predicate = cb.and(predicates.toArray(new Predicate[predicates.size()]));
return predicate;
}
};
}
Please, forgive me for the simple use case, but I hope you get the idea.
The solution proposed in his comment by #OsamaAbdulRehman looks interesting as well, although I honestly never tested it.
I am doing a home assignment where I have 2 classes, which are User and Car. User has a OneToMany relationship to Car entity, therefore I need to implement a controller method, which will be selecting all cars of a special user (by ID)
I guess it has something todo with #Query annotation in UserRepository which extends JpaRepository.
I am using a JpaRepository, which has generics.
Example: GET method - /users/{id}/cars
The data should be received in JSON format smth like that:
{
"id":"1",
"name":"Taavet Prulskih",
"cars":[{
"Id":"1",
"make":"BMW",
"model":"760",
"numberplate":"123FFF"
},
{
"Id":"2",
"make":"Opel",
"model":"Astra",
"numberplate":"789BFX"
}]
}
Question is: how does the query will be looking like?
You can use SpringData query methods, just create new method in CarRepository like that:
List<Car> findByUser_id(String id);
More examples in Query methods documentation.
You can create an UserDTO like this:
private static class UserDTO(){
private Long id;
private String name;
private List<Cars> cars;
//getters and setters
}
With JPA you can get the user that you want, in your repository:
User findByUserId(Long id)
And then fill the DTO like this:
UserDTO dto = new UserDTO();
User user = repository.findByUserId(id);
dto.setId(user.getUserId);
dto.setName(user.getName);
dto.setCars(user.getCars); //get the list of cars of the user and sets entire list to dto.cars
return dto;//finally return dto to controller
I have User class like this :
#Data
#Entity
public class User {
#Id #GeneratedValue Long userID;
String eMail;
String passwordHash;
}
And I have data like this :
[{"userID":1,"passwordHash":"asdasd","email":"admin#admin.com"},
{"userID":2,"passwordHash":"12345","email":"admin1asdasd#admin.com"}]
I have two method in my controller class, one - to get single user :
#GetMapping("/user/{id}")
User one(#PathVariable Long id) {
return repository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
Other method to retrieve all user :
#GetMapping("/user")
List<User> all() {
return repository.findAll();
}
In my browser, going to this address - http://localhost:8080/user , I can see these data. And if I goto http://localhost:8080/user/id I can get a specific value.
Now my question is how can access data like http://localhost:8080/user/email/passwordHash? I am quite sure that it is not possible, because I haven't stored data in that way.
As my main target is to verify login, I have already written a #Query in my repository interface. Here it is :
public interface UserRepository extends JpaRepository<User, Long> {
#Query("select u from User u where u.eMail = ?1 and u.passwordHash = ?2")
List<User> listByLoginCredential(String emailAddress,String passwordHash);
}
Can Anyone tell me how can I do this,use this method of this interface?
I think you can can achieve what you want by adding the following method to the controller class:
#GetMapping("/user/{emailAddress}/{passwordHash}")
List<User> byMailAndPassword(#PathVariable String emailAddress, #PathVariable String passwordHash) {
return repository.listByLoginCredential(emailAddress, passwordHash)
}
On the other hand you say that your main goal is to verify login, so it looks like you are doing authentication. If you have time you should look into doing it with spring security https://spring.io/guides/gs/securing-web/#initial
Maybe this help https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.
Or you can also create procedure in Database and call stored procedure with Spring boot.
Login is related to security so ideally you should create a separate post method and use the repository method. Always make sure to follow the best practice.
Spring security is something you can utilize for better authentication and authorization.
For example, I've got such query:
#Query("SELECT u.name FROM User u")
public List<User> findAllUsers();
I know, this is not an optimal way to show all users, but it's just for example, my query is more complex.
I've got such answer:
[{"Tom Wally"}]
But I want to get:
[{"name":"Tom Wally"}]
How to add column name to custom query?
Maybe you are talking about how return a response in a json format.
If you want a response in json-format you should create two classes like these, one when creating your object, and the other when creating the response from a list.
public class UserResponseList extends ArrayList<UserResponse>(){
public UserResponseList(List <UserResponse> myList){
super(myList);
}
}
public class UserResponse(){
private String name;
}
finally ,you instantiate UserResponseList sending your list in constructor, and you have your json response with your specific format.