I have this code:
#GetMapping("/notes/{id}")
public ResponseEntity<Note> getNoteById(#PathVariable(value = "id") Long id) {
Note note = noteRepository.findOne(id);
if(note == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok().body(note);
}
So this method just finds information by the sent id.
The method noteRepository.findOne() accepts only Long or class extends org.springframework.data.domain.Example
I want to retrive data by my own variable "secretkey" (String). How can I do this?
Use this method :
#GetMapping("/notes/byKey/{secretKey}")
public ResponseEntity<Note> getNoteById(#PathVariable(value = "secretKey") String secretKey) {
Note note = noteRepository.findBySecretKey(secretKey);
if(note == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok().body(note);
}
in your NoteRepositoryclass add this method:
Note findBySecretKey(String secretKey);
note that I also changed the request path.
Assume that you are using Spring Data:
#Entity
#Table(name="note")
public class Note {
#Id
#GeneratedAuto
private Long id;
#Column(name="secretKey")
private String secretKey;
// getters, setters
}
//Repository
public interface NoteRepository extends JpaRepository<Note, Long> {
List<Note> findBySecretKey(String secretKey);
}
// Controller
#GetMapping(path = "/notes/secretKey/{secretKey}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Note> getNoteBySecretKey(
#PathVariable(value = "secretKey") String secretKey) {
Note note = noteRepository.findBySecretKey(secretKey);
if(note == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok().body(note);
}
Related
I'm trying to set a defaultValue for a boolean field using MapStruct, but the generated code simply ignores it.
My code:
public class CreateEventRequest {
#NotNull
#JsonProperty
private Boolean isPrivate;
#JsonProperty
private Boolean friendCanInviteFriends;
#JsonProperty
private boolean showGuestList;
public boolean isPrivate() {
return isPrivate;
}
public String getDescription() {
return description;
}
public boolean isFriendCanInviteFriends() {
return friendCanInviteFriends;
}
public boolean isShowGuestList() {
return showGuestList;
}
}
My mapper:
#Mapper(componentModel = "spring")
public interface CreateEventRequestMapper {
#Mapping(target = "showGuestList", source = "showGuestList", defaultExpression = "java(true)")
#Mapping(target = "friendCanInviteFriends", source = "friendCanInviteFriends", defaultValue = "true")
Event map(CreateEventRequest request);
}
The generated code:
public class CreateEventRequestMapperImpl implements CreateEventRequestMapper {
#Override
public Event map(CreateEventRequest request) {
if ( request == null ) {
return null;
}
Event event = new Event();
event.setShowGuestList( request.isShowGuestList() );
event.setFriendCanInviteFriends( request.isFriendCanInviteFriends() );
event.setPrivate( request.isPrivate() );
return event;
}
}
As you can see, I've tried using primitive/non-primitive type but it simply ignores the defaultValue.
Am I missing something here?
Thanks!
The problem is that the return type of your getter methods in the source object is always primitive which can't be null, you need to return Boolean.
MapStruct doesn't support direct private field access which requires reflection.
I have below class referencing to itself:
#Entity
#Inheritance(strategy = TABLE_PER_CLASS)
//#JsonIdentityInfo(property="rowId", generator = ObjectIdGenerators.PropertyGenerator.class)
public abstract class AbstractEntity implements Serializable {
/**
*
*/
private static final long serialVersionUID = 568799551343430329L;
#OneToOne(optional=false, fetch=FetchType.EAGER)
#JoinColumn(name="createdBy")
protected User createdBy;
#OneToOne(optional=false, fetch=FetchType.EAGER)
#JoinColumn(name="lastUpdatedBy")
protected User lastUpdatedBy;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(unique = true, nullable = false, updatable = false, length = 7)
private Integer rowId;
public User getCreatedBy() {
return this.createdBy;
}
public void setCreatedBy(User createdBy) {
this.createdBy = createdBy;
}
public User getLastUpdatedBy() {
return this.lastUpdatedBy;
}
public void setLastUpdatedBy(User lastUpdatedBy) {
this.lastUpdatedBy = lastUpdatedBy;
}
public Integer getRowId() {
return this.rowId;
}
public void setRowId(Integer RowId) {
this.rowId = RowId;
}
public String toString() {
return "[Id]:" + this.rowId + " - [CreatedBy]:" + this.createdBy;
}
}
Then I have a class User extending this class and a RepositoryUser interface:
public interface RepositoryUser extends CrudRepository<User, Integer> {
}
And a Controller:
#Controller
#RequestMapping(path = "/user")
public class ServiceUser {
#Autowired
private RepositoryUser repositoryUser;
#GetMapping(path="/all", produces = "application/json; charset=UTF-8", headers = "Accept=application/json")
public #ResponseBody Iterable<User> getAllUsers() {
return repositoryUser.findAll();
}
#PostMapping(path="/add", consumes="application/json")
public #ResponseBody User createOneUser(#RequestBody User user) {
System.out.println(user);
return repositoryUser.save(user);
}
}
My issue is that I'm making reference to User twice (createdby and lastupdatedby) in the same class and either I tried JSonIdentityInfo, Jsonmanaged,jsonback nothing works. correctly.
I need to be able to have
{
User 1 data including created by and last updated by
User 2 data including created by and last updated by
}
and when I add I need to set the user who creates the record.
Can you please help me ?
Thanks a lot!
You could write/try a Custom Serializer Using StdSerializer.
Example of CustomJsonSerializer. NB: Did not run the code.
public class CustomJsonSerializer extends StdSerializer<AbstractEntity> {
public CustomJsonSerializer() {
this(null);
}
public CustomJsonSerializer(Class<AbstractEntity> t) {
super(t);
}
#Override
public void serialize(AbstractEntity value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
Field[] fields = value.getClass().getDeclaredFields();
jgen.writeStartObject();
for (Field field : fields) {
field.setAccessible(true);
try {
// Do the proper field mapping for field types . Object type example
jgen.writeObjectField(field.getName(), field.get(value));
} catch (Exception e) {
// catch error
}
}
jgen.writeEndObject();
}
}
Then on your Rest Method use #JsonSerialize
#JsonSerialize(using = CustomJsonSerializer.class)
#GetMapping(path="/all", produces = "application/json; charset=UTF-8", headers = "Accept=application/json")
public #ResponseBody Iterable<User> getAllUsers() {
return repositoryUser.findAll();
}
Please see Custom Serializer And StdSerializer
Possible different solution
jackson-bidirectional infinite-recursion
My question is actually a spin-off of this question as seen here... so it might help to check that thread before proceeding.
In my Spring Boot project, I have two entities Sender and Recipient which represent a Customer and pretty much have the same fields, so I make them extend the base class Customer;
Customer base class;
#MappedSuperclass
public class Customer extends AuditableEntity {
#Column(name = "firstname")
private String firstname;
#Transient
private CustomerRole role;
public Customer(CustomerRole role) {
this.role = role;
}
//other fields & corresponding getters and setters
}
Sender domain object;
#Entity
#Table(name = "senders")
public class Sender extends Customer {
public Sender(){
super.setRole(CustomerRole.SENDER);
}
}
Recipient domain object;
#Entity
#Table(name = "recipients")
public class Recipient extends Customer {
public Recipient(){
super.setRole(CustomerRole.RECIPIENT);
}
}
NOTE - Sender and Recipient are exactly alike except for their roles. These can be easily stored in a single customers Table by making the Customer base class an entity itself, but I intentionally separate the entities this way because I have an obligation to persist each customer type in separate database tables.
Now I have one form in a view that collects details of both Sender & Recipient, so for example to collect the firstname, I had to name the form fields differently as follows;
Sender section of the form;
<input type="text" id="senderFirstname" name="senderFirstname" value="$!sender.firstname">
Recipient section of the form;
<input type="text" id="recipientFirstname" name="recipientFirstname" value="$!recipient.firstname">
But the fields available for a customer are so many that I'm looking for a way to map them to a pojo by means of an annotation as asked in this question here. However, the solutions provided there would mean that I have to create separate proxies for both domain objects and annotate the fields accordingly e.g
public class SenderProxy {
#ParamName("senderFirstname")
private String firstname;
#ParamName("senderLastname")
private String lastname;
//...
}
public class RecipientProxy {
#ParamName("recipientFirstname")
private String firstname;
#ParamName("recipientLastname")
private String lastname;
//...
}
So I got very curious and was wondering, is there a way to map this Proxies to more than one #ParamName such that the base class for example can just be annotated as follows?;
#MappedSuperclass
public class Customer extends AuditableEntity {
#Column(name = "firstname")
#ParamNames({"senderFirstname", "recipientFirstname"})
private String firstname;
#Column(name = "lastname")
#ParamNames({"senderLastname", "recipientLastname"})
private String lastname;
#Transient
private CustomerRole role;
public Customer(CustomerRole role) {
this.role = role;
}
//other fields & corresponding getters and setters
}
And then perhaps find a way to select value of fields based on annotation??
A suggestion from Zhang Jie like ExtendedBeanInfo
so i do it this way
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Alias {
String[] value();
}
public class AliasedBeanInfoFactory implements BeanInfoFactory, Ordered {
#Override
public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
return supports(beanClass) ? new AliasedBeanInfo(Introspector.getBeanInfo(beanClass)) : null;
}
private boolean supports(Class<?> beanClass) {
Class<?> targetClass = beanClass;
do {
Field[] fields = targetClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Alias.class)) {
return true;
}
}
targetClass = targetClass.getSuperclass();
} while (targetClass != null && targetClass != Object.class);
return false;
}
#Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 100;
}
}
public class AliasedBeanInfo implements BeanInfo {
private static final Logger LOGGER = LoggerFactory.getLogger(AliasedBeanInfo.class);
private final BeanInfo delegate;
private final Set<PropertyDescriptor> propertyDescriptors = new TreeSet<>(new PropertyDescriptorComparator());
AliasedBeanInfo(BeanInfo delegate) {
this.delegate = delegate;
this.propertyDescriptors.addAll(Arrays.asList(delegate.getPropertyDescriptors()));
Class<?> beanClass = delegate.getBeanDescriptor().getBeanClass();
for (Field field : findAliasedFields(beanClass)) {
Optional<PropertyDescriptor> optional = findExistingPropertyDescriptor(field.getName(), field.getType());
if (!optional.isPresent()) {
LOGGER.warn("there is no PropertyDescriptor for field[{}]", field);
continue;
}
Alias alias = field.getAnnotation(Alias.class);
addAliasPropertyDescriptor(alias.value(), optional.get());
}
}
private List<Field> findAliasedFields(Class<?> beanClass) {
List<Field> fields = new ArrayList<>();
ReflectionUtils.doWithFields(beanClass,
fields::add,
field -> field.isAnnotationPresent(Alias.class));
return fields;
}
private Optional<PropertyDescriptor> findExistingPropertyDescriptor(String propertyName, Class<?> propertyType) {
return propertyDescriptors
.stream()
.filter(pd -> pd.getName().equals(propertyName) && pd.getPropertyType().equals(propertyType))
.findAny();
}
private void addAliasPropertyDescriptor(String[] values, PropertyDescriptor propertyDescriptor) {
for (String value : values) {
if (!value.isEmpty()) {
try {
this.propertyDescriptors.add(new PropertyDescriptor(
value, propertyDescriptor.getReadMethod(), propertyDescriptor.getWriteMethod()));
} catch (IntrospectionException e) {
LOGGER.error("add field[{}] alias[{}] property descriptor error", propertyDescriptor.getName(),
value, e);
}
}
}
}
#Override
public BeanDescriptor getBeanDescriptor() {
return this.delegate.getBeanDescriptor();
}
#Override
public EventSetDescriptor[] getEventSetDescriptors() {
return this.delegate.getEventSetDescriptors();
}
#Override
public int getDefaultEventIndex() {
return this.delegate.getDefaultEventIndex();
}
#Override
public PropertyDescriptor[] getPropertyDescriptors() {
return this.propertyDescriptors.toArray(new PropertyDescriptor[0]);
}
#Override
public int getDefaultPropertyIndex() {
return this.delegate.getDefaultPropertyIndex();
}
#Override
public MethodDescriptor[] getMethodDescriptors() {
return this.delegate.getMethodDescriptors();
}
#Override
public BeanInfo[] getAdditionalBeanInfo() {
return this.delegate.getAdditionalBeanInfo();
}
#Override
public Image getIcon(int iconKind) {
return this.delegate.getIcon(iconKind);
}
static class PropertyDescriptorComparator implements Comparator<PropertyDescriptor> {
#Override
public int compare(PropertyDescriptor desc1, PropertyDescriptor desc2) {
String left = desc1.getName();
String right = desc2.getName();
for (int i = 0; i < left.length(); i++) {
if (right.length() == i) {
return 1;
}
int result = left.getBytes()[i] - right.getBytes()[i];
if (result != 0) {
return result;
}
}
return left.length() - right.length();
}
}
}
Ok so I am new to spring and don't really know how this works. I have been trying a few things and think its close to doing it but not getting any data from the server and giving me this error
Unsatisfied dependency expressed through constructor argument with index 4 of type [jp.co.fusionsystems.dimare.crm.service.impl.MyDataDefaultService]: : Error creating bean with name 'MyDataDefaultService' defined in file
My end point
//mobile data endpoint
#RequestMapping(
value = API_PREFIX + ENDPOINT_MyData + "/getMyData",
method = RequestMethod.GET)
public MyData getMyData() {
return MyDataDefaultService.getData();
}
My Object
public class MyData {
public MyData(final Builder builder) {
videoLink = builder.videoLink;
}
private String videoLink;
public String getVideoLink()
{
return videoLink;
}
public static class Builder
{
private String videoLink = "";
public Builder setVideo(String videoLink)
{
this.videoLink = videoLink;
return this;
}
public MyData build()
{
return new MyData(this);
}
}
#Override
public boolean equals(final Object other) {
return ObjectUtils.equals(this, other);
}
#Override
public int hashCode() {
return ObjectUtils.hashCode(this);
}
#Override
public String toString() {
return ObjectUtils.toString(this);
}
}
The Repository
public classMyServerMyDataRepository implements MyDataRepository{
private finalMyServerMyDataJpaRepository jpaRepository;
private final MyDataConverter MyDataConverter = new MyDataConverter();
#Autowired
publicMyServerMyDataRepository(finalMyServerMyDataJpaRepository jpaRepository) {
this.jpaRepository = Validate.notNull(jpaRepository);
}
#Override
public MyData getData() {
MyDataEntity entity = jpaRepository.findOne((long) 0);
MyData.Builder builder = new MyData.Builder()
.setVideo(entity.getVideoLink());
return builder.build();
}
The DefaultService that gets called by the endpoint
public class MyDataDefaultService {
private static final Logger logger = LoggerFactory.getLogger(NotificationDefaultService.class);
private finalMyServerMyDataRepository repository;
#Autowired
public MyDataDefaultService(MyServerMyDataRepository repository) {
this.repository = Validate.notNull(repository);
}
//Get the data from the server
public MobileData getData()
{
logger.info("Get Mobile Data from the server");
//Get the data from the repository
MobileData mobileData = repository.getData();
return mobileData;
}
}
The Converter
public class MyDataConverter extends AbstractConverter<MyDataEntity, MyData>
{
#Override
public MyData convert(MyDataEntity entity) {
MyData.Builder builder = new MyData.Builder()
.setVideo(entity.getVideoLink());
return builder.build();
}
}
My Entity
#Entity
#Table(name = “myServer”)
public class MyDataEntity extends AbstractEntity{
#Column(name = "video_link", nullable = true)
private String videoLink;
public String getVideoLink() {
return videoLink;
}
public void setVideoLink(final String videoLink) {
this.videoLink = videoLink;
}
}
Thank you for any help with this
Hibernate entity should have default constructor defined and implement Serializable interface as well, assume AbstractEntity matches the requirement. Hibernate won't accept an entity without a primary key so you have to define the one too:
#Entity
#Table(name = “myServer”)
public class MyDataEntity implements Serializable {
#Id
#GeneratedValue
private Long id;
#Column(name = "video_link", nullable = true)
private String videoLink;
public MyDataEntity() {
}
...setters&getters
}
MyData object represents the JSON server response, you can use Jackson annotations to control the result JSON properties:
public class MyDataResponse {
#JsonProperty("video_link")
private String videoLink;
public MyDataResponse() {
}
public MyDataResponse(String videoLink) {
this.videoLink = videoLink;
}
...setters&getters
}
Spring has an awesome project so called Spring Data that provides the JPA repositories, so there's no even the #Repository annotation ever needed:
public class MyDataRepository extends CrudRepository<MyDataEntity, Long> {
}
The Builder class represents the Service layer:
#Service
public class MyDataService {
#Autowired
private MyDataRepository myDataRepository;
public MyDataResponse getMyData(Long id) {
MyDataEntity entity = myDataRepository.findOne(id);
...rest logic, copy necessary data to MyDataResponse
}
}
Then a controller is:
#RestController // #ResponseBody not needed when using like this
public MyDataController {
#Autowired
private MyDataService myDataService;
#RequestMapping("/getMyData") // no need to specify method for GET
public MyDataResponse getMyData(#RequestParam("ID") Long myDataId) {
... validation logic
return myDataService.getMyData(myDataId); // return response
}
}
Now it should work, don't forget to add required dependencies to your classpath.
I am using Spring REST with Hibernate and i am fetching a particular record from database table passing id into my method. The method is working properly but if there is no record in table then i want false in a variable and if record exist then i want true in the variable in my json object.
Here is my Entity class Subscribe.java
#Entity
#Table(name="subscribe")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Subscribe implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="id")
private long id;
#Column(name="subscribed_id")
private String subID;
#Column(name="subscriber_id")
private long subrID;
public long getSubrID() {
return subrID;
}
public void setSubrID(long subrID) {
this.subrID = subrID;
}
public String getSubID() {
return subID;
}
public void setSubID(String subID) {
this.subID = subID;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
Here is my DAO class
#SuppressWarnings({ "unchecked", "rawtypes" })
public List<Subscribe> getSubscribeById(long id) throws Exception {
session = sessionFactory.openSession();
Criteria cr = session.createCriteria(Subscribe.class);
cr.add(Restrictions.eq("subrID", id));
List results = cr.list();
tx = session.getTransaction();
session.beginTransaction();
tx.commit();
return results;
}
And here is my Controller
#RequestMapping(value = "/subscribe/{id}", method = RequestMethod.GET)
public #ResponseBody
List<Subscribe> getSubscriber(#PathVariable("id") long id) {
List<Subscribe> sub = null;
try {
sub = profileService.getSubscribeById(id);
} catch (Exception e) {
e.printStackTrace();
}
return sub;
}
Please suggest me how can can i do this
Given the way your code is structured ( where you effectively pass the database object directly to the REST client ) there is no clean way that you can do this.
I think a more RESTful approach would be to return an HTTP code that indicates that the requested record could not be found. HTTP 404 would be the appropriate code to use.
So, in pseudo-code,
#RequestMapping(value = "/subscribe/{id}", method = RequestMethod.GET)
public #ResponseBody
List<Subscribe> getSubscriber(#PathVariable("id") long id) {
List<Subscribe> sub = null;
try {
sub = profileService.getSubscribeById(id);
} catch (Exception e) {
e.printStackTrace();
}
if ( sub.isEmpty() ){
return HTTP 404 result code
} else {
return sub;
}
}
Your client side code will need to be altered to respond to the HTTP result code rather than a boolean value, but otherwise would remain unchanged.