MissingPropertyException: No such property for class

In previous article, we are using Groovy Templates for Spring Boot CRUD application. We have Movie entity:

...
@Entity
@Getter
@Setter
public class Movie implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    
    @NotBlank
    @Size(max = 255)
    private String title;
    
    private Integer releaseYear;
    
    private Integer runtimeMinutes;
    
    @Size(max = 255)
    private String tags;
    
    @Size(max = 50)
    private String lang;
    
    @Size(max = 50)
    private String country; 
}
                    

Please refer here for full entity class.

Previously, this is the snippet for getMovieById(...) function:

@GetMapping(value = "/movies/{movieId}")
public String getMovieById(Model model, @PathVariable long movieId) {
    Movie movie = null;
    String errorMessage = null;
    try {
        movie = movieService.findById(movieId);
    } catch (Exception ex) {
        errorMessage = "Movie not found";
    }

    model.addAttribute("movie", movie);
    model.addAttribute("allowDelete", false);
    model.addAttribute("errorMessage", errorMessage);
    return "movie";
}
                    

And this is a snippet of movie.tpl:

yieldUnescaped '<!DOCTYPE html>'
html(lang:'en') {
    head {
        ...
    }
    body {
        ...
        div {
            table(border: "0") {
                tr {
                    td("Id")
                    td(":")
                    td(movie.id ?: '')
                }
                tr {
                    td("Title")
                    td(":")
                    td(movie.title ?: '')
                }
                tr {
                    td("Release Year")
                    td(":")
                    td(movie.releaseYear ?: '')
                }
                tr {
                    td("Runtime (Minutes)")
                    td(":")
                    td(movie.runtimeMinutes ?: '')
                }
                tr {
                    td("Tags")
                    td(":")
                    td(movie.tags ?: '')
                }
                tr {
                    td("Language")
                    td(":")
                    td(movie.lang ?: '')
                }
                tr {
                    td("Country")
                    td(":")
                    td(movie.country ?: '')
                }
            }
        }
        ...
    }
}
                    

Check here for complete template source.

The Problem

When running the application and browse one movie, one example in http://localhost:8080/movies/2, I got following error:

groovy.lang.MissingPropertyException: No such property: id for class: movie Possible solutions: out at groovy.lang.MetaClassImpl.invokeStaticMissingProperty(MetaClassImpl.java:1021) ~[groovy-2.5.8.jar:2.5.8] at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:1866) ~[groovy-2.5.8.jar:2.5.8] at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:1842) ~[groovy-2.5.8.jar:2.5.8] at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:3797) ~[groovy-2.5.8.jar:2.5.8] at org.codehaus.groovy.runtime.callsite.ClassMetaClassGetPropertySite.getProperty(ClassMetaClassGetPropertySite.java:50) ~[groovy-2.5.8.jar:2.5.8] at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGetProperty(AbstractCallSite.java:298) ~[groovy-2.5.8.jar:2.5.8] at movie$_run_closure1$_closure3$_closure4$_closure7$_closure8.doCall(movie.tpl:18) ~[na:na] at movie$_run_closure1$_closure3$_closure4$_closure7$_closure8.call(movie.tpl) ~[na:na]

When check in the internet, I find another people with similar problem:

https://stackoverflow.com/questions/48995756/spring-boot-groovy-template-no-such-property-for-class-whe

Solution

The problem is because the name of the template we used. If we change template movie.tpl into something else: movie-view.tpl, and change getMovieById(...) to return "movie-view".

@GetMapping(value = "/movies/{movieId}")
public String getMovieById(Model model, @PathVariable long movieId) {
    ...
    model.addAttribute("movie", movie);
    ...
    return "movie-view";
}
                    

And when run it:

http://localhost:8080/movies/2

http://localhost:8080/movies/2

Problem solved! Maybe the engine confused since "movie" is used both as name of the template and model.